Search code examples
c#kendo-uikendo-asp.net-mvc

Prevent ClientTemplate from overriding EditorTemplate in Grid Form


I have a Kendo UI ASP.NET MVC grid with CRUD operations that is being submitted as part of a form. In the following snippet, I am trying to display a column with an inline editable combobox (bound to user names and IDs) that displays a user's name but has a value of the user's ID.

Submits form properly, but displays the user IDs rather than names:

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 EditorTemplate:

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

If I remove the ClientTemplate that provides the form input tag, the user's name is what is displayed rather than the value of the user's ID which is what I want. However, I need to submit it in batch as a part of a form, so I cannot remove the form input tag.

Fails to submit the form (no input tag), but displays the user names rather than IDs correctly:

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

What solution can I use to combine these two requirements so that the grid column displays names (but with values of IDs) and also provides the form input tag?


Solution

  • First of all, the ClientTemplate is not overriding the EditorTemplate...

    The ClientTemplate is essentially the display template and the EditorTemplate is, well, the editor template.

    So, what you want is the ClientTemplate to do is display the name associated with the UserId.

    If you want to keep the grid the way you have it(using ForeignKey and hidden fields), then you need to add a "mapping" function that maps the UserId selected by the combobox to the associated name.

    You can do this like so:

    Razor:

    .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;
    }
    

    This uses the Id-Name array that you already have(via grid.options.columns.values) that was created by the ForeignKey() method.

    Alternate solutions . . . . . There are other ways to achieve what you want, though.

    One way is to not use a ForeignKey and instead bind the column to an object that contains both the ID and the Name and the more specific EditorTemplate binds to the same collection that ForeignKey() was. This does not require the mapping function, i.e:

    Razor:

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

    EditorTemplate:

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

    The downside to hidden input method is that you must limit the grid to a small number of rows as the technique will only be able to post a single page's worth of data as the data on pages 2, 3, etc are not rendered and thus have no hidden inputs in the form to be included in the post.

    A third way(this is the way I do things) is to not use the hidden inputs at all. Instead, I post the data manually with an ajax call where I first serialize the "main" form data(not grid data) into an object(this is super easy if you use kendo MVVM) and then merge the modified grid data with the form data(using extend) and then post the resulting combined object.

    I use a variation of the this technique to serialize the grid data: http://www.telerik.com/support/code-library/save-all-changes-with-one-request

    The benefits of this are that it supports larger datasets as it can use the grid paging mechanism and(this is the main reason I like this technique) by submitting the form/header/parent data with all the grid/child data(new/updated/deleted) in a single request, you can process all the data at the same time on the server, allowing for better validation of the entire "picture" of the data as well as allowing the updates to occur in a single transaction bracket.