How To Fix Kendo Grid with ClientDetailTemplate Property is Not Defined

I worked with a Kendo Grid today that used MVC wrappers to define a client detail template and ran into a very frustrating problem that I thought I’d post about and hopefully save someone else hours of head-banging.

For anyone who has spent much time with Kendo, you know that the documentation can be quite vague and not go into a lot of detail. It often leaves out important details that you end up finding buried in a forum post or on StackOverflow. That happened again today.

The situation was pretty simple. I had a Grid of items and wanted to use a ClientDetailTemplate to show another Grid of child items under the parent item. It looked similar to this:

@(Html.Kendo().Grid<Kendo.Mvc.Examples.Models.CategoryViewModel>()
    .ClientDetailTemplateId("product-template")
    .Name("grid")
    .DataSource(dataSource => dataSource
    	.Ajax()
        .Read(read => read.Action("Categories_Read", "Home"))
    )
    .Columns(columns =>
    {
    	columns.Bound(product => product.CategoryID);
        columns.Bound(product => product.CategoryName);
    })
)

Then I defined the client detail template. Note two things: 1) you can use the scoped parent’s properties in the name of the grid (e.g. #=CategoryId#), and 2) you can’t have any spaces around that template code in the grid name, or it will throw an error.

Here’s what my defined template looked like:

<script id="sample-template" type="text/kendo-tmpl">
    @(Html.Kendo().Grid<Kendo.Mvc.Examples.Models.ProductViewModel>()
            .Name("client-detail-grid-#=CategoryID#")
            .Columns(columns =>
            {
                columns.Bound(c => c.Name).Title("Product Name");
                columns.Bound(c => c.ProductID).ClientTemplate(string.Format("<li>{0}</li>", Html.ActionLink("Edit", "Edit", "Products", new { id = "id_temp" }, null).ToHtmlString().Replace("id_temp", "#=ProductID#")))
            })
            .DataSource(dataSource => dataSource
            	.Ajax()
                .Read(read => read.Action("Products_Read", "Home"))
            )
            .ToClientTemplate()
        )
</script>

I thought this was pretty simple, right? Wrong. I kept getting an error message in the Console like:

Uncaught ReferenceError: ProductID is not defined

Huh? I could see the Products were coming across in the AJAX request, with the ProductID defined, but it’s like the template couldn’t find or read the property.

As has happened so many times for me, I stumbled upon the answer buried in a Telerik forum post. The answer even came with a smiley face, which immediately made me giggle and skip around the room.

Child Grids are designed to have the ability to get its parent’s property values through client templates using the syntax #=ParentProperty#. But when calling its own property values, the syntax \\#=OwnProperty\\# is used rather. 🙂

Justin don't think that's funny

No, I lied — I did not giggle, nor did I skip. So in a client detail template, it is scoped to the parent by default, but you can access the child properties by double escaping the hash first.

So our example from above becomes:

<script id="sample-template" type="text/kendo-tmpl">
    @(Html.Kendo().Grid<Kendo.Mvc.Examples.Models.ProductViewModel>()
            .Name("client-detail-grid-categories#=CategoryID#")
            .Columns(columns =>
            {
                columns.Bound(c => c.Name).Title("Product Name");
                columns.Bound(c => c.ProductID).ClientTemplate(string.Format("<li>{0}</li>", Html.ActionLink("Edit", "Edit", "Products", new { id = "id_temp" }, null).ToHtmlString().Replace("id_temp", "\\#=ProductID\\#")))
            .DataSource(dataSource => dataSource
            	.Ajax()
                .Read(read => read.Action("Products_Read", "Home"))
            )
            .ToClientTemplate()
        )
</script>

That’s it. I’m done with you for tonight, Kendo. You got me again.

Your Turn!

Have you run into this problem before, or something like it? Have you found yourself frustrated with Kendo’s documentation too? Let’s talk about it below in the comments!