防止 ClientTemplate 覆盖 Grid Form 中的 EditorTemplate

Prevent ClientTemplate from overriding EditorTemplate in Grid Form

我有一个 Kendo UI ASP.NET MVC 网格,其中包含作为表单的一部分提交的 CRUD 操作。在下面的代码片段中,我试图显示一个包含内联可编辑组合框(绑定到用户名和 ID)的列,该组合框显示用户名但具有用户 ID 的值。

正确提交表单,但显示用户 ID 而不是名称:

columns.ForeignKey(p => p.UserId, (System.Collections.IEnumerable)ViewBag.Users, "SystemUserId", "Name")
    .Title("User")
    .EditorTemplateName("ComboBoxInForm")
    .Visible(true)
    .ClientTemplate("#= UserId #" + "<input type='hidden' name='Users[#= index(data) #].UserId' value='#= UserId #' />"
);

ComboBoxInForm 编辑器模板:

@model object 
@(Html.Kendo().ComboBoxFor(m => m)
    .BindTo((SelectList)ViewData[ViewData.TemplateInfo.GetFullHtmlFieldName("") + "_Data"])
)

如果我去掉提供表单输入标签的ClientTemplate,显示的是用户名,而不是我想要的用户ID值。但是,我需要作为表单的一部分批量提交,所以我无法删除表单输入标签。

提交表单失败(无输入标签),但正确显示用户名而不是 ID:

columns.ForeignKey(p => p.UserId, (System.Collections.IEnumerable)ViewBag.Users, "SystemUserId", "Name")
    .Title("User")
    .EditorTemplateName("ComboBoxInForm")
    .Visible(true);

我可以使用什么解决方案来结合这两个要求,以便网格列显示名称(但带有 ID 值)并提供表单输入标签?

首先,ClientTemplate 没有覆盖 EditorTemplate...

ClientTemplate 本质上是 display 模板,而 EditorTemplate 是 editor 模板。

因此,您希望 ClientTemplate 执行的操作是显示与 UserId 关联的 name

如果您想保持网格的原样(使用 ForeignKey 和隐藏字段),则需要添加一个 "mapping" 函数,将组合框选择的 UserId 映射到关联的名称。

你可以这样做:

剃须刀:

.ClientTemplate("#= mapIdToName(UserId) #" +
    "<input type='hidden' name='Users[#= index(data) #].UserId' value='#= UserId #' />");

Javascript:

function mapIdToName(id) {
    var grid = $("#grid").getKendoGrid(),
        users = grid.options.columns[0].values,
        name;

    for (var i = 0; i < users.length; i++) {
        if (users[i].value == id) {
            name = users[i].text;
            break;
        }
    }

    return name;
}

这使用您已经拥有的(通过 grid.options.columns.values)通过 ForeignKey() 方法创建的 Id-Name 数组。

替代解决方案 . . . . . 不过,还有其他方法可以实现您想要的效果。

一种方法是不使用 ForeignKey,而是将列绑定到包含 ID 和名称的对象,更具体的 EditorTemplate 绑定到与 ForeignKey() 相同的集合。这不需要映射函数,即:

剃须刀:

columns.Bound(x => x.User)
    .ClientTemplate("#= User.Name #" +
    "<input type='hidden' name='Users[#= index(data) #].UserId' value='#= UserId #' />");

编辑器模板:

@model object
@(
 Html.Kendo().ComboBoxFor(m => m)
 .DataValueField("SystemUserId")
    .DataTextField("Name")
    .BindTo((System.Collections.IEnumerable)ViewBag.Users)
)

隐藏输入法的缺点是您必须将网格限制为少量行,因为该技术只能 post 单页数据作为第 2 页上的数据, 3 等未呈现,因此在要包含在 post.

中的表单中没有隐藏输入

第三种方式(这是我做事的方式)是根本不使用隐藏的输入。 相反,我通过 ajax 调用手动 post 数据,我首先将 "main" 表单数据(不是网格数据)序列化为一个对象(如果使用 [=64 这非常容易=] MVVM),然后将修改后的网格数据与表单数据合并(使用扩展),然后 post 生成的组合对象。

我使用此技术的变体来序列化网格数据:http://www.telerik.com/support/code-library/save-all-changes-with-one-request

这样做的好处是它支持更大的数据集,因为它可以使用网格分页机制,并且(这是我喜欢这种技术的主要原因)通过提交 form/header/parent 数据和所有 grid/child data(new/updated/deleted) 在单个请求中,您可以在服务器上同时处理所有数据,从而更好地验证整个 "picture" 数据并允许更新发生在单个交易范围内。

另一个答案from the Telerik forums

出现此行为的原因是 ForeignKey 构建器确实提供显示与数字值关联的文本作为内置功能。在引擎盖下,该列绑定到仅包含数值的字段。为了在您的客户端模板中显示文本表示,应该编写一些自定义逻辑。如果这可能对面临同样困难的其他人有所帮助,我会提供一些关于如何实现这一目标的指导方针:

  1. 输入元素的值可以定义为接受实际字段值作为第一个参数和字段名称(静态文本)作为第二个参数的函数。
  2. Telerik MVC 包装器呈现初始化 Kendo UI 小部件所需的 HTML 和 JavaScript。每个外键列只是存储在 columns.values.
  3. 中的键值对数组
  4. 这是一个带有一些注释的函数体,可以帮助您说明从 columns.values 中查找与特定数字(当前字段值)关联的文本的简单想法:

    function textValue(value, fieldName) {
      // $("#gridNameId").data("kendoGrid").options.columns contains the
      // columns defined in the grid's configuration
    
      // iterate through the array and check the .field value of each column to find
      // a column field with same name as the field name parameter (second parameter)
    
      // each column which is defined as "foreign key" does have values array. 
      // It contains key-value pairs of each values displayed in the combo box.
    
      // return the text value associated with same value as the first first argument (value)
      // return text associated with the value
    }
    
    ClientTemplate("#= ProductID #" +
    "<input type='hidden' name='Products[#= index(data)#].ProductID' value='#= textValue(data.ProductID, \'ProductID\') #' />"