Is there a way to place a Kendo UI grid within a template that is used as the detail row? I need to be able to dynamically determine if a grid should be in a detail row template, and adjust the UI accordingly. Specifically, if there is a subcategory record, I want to display the sub categories within a new hierarchical grid nested in the outside grid. I found the following example, but it uses Angular. I need to do something like this with Kendo UI for jQuery.
Thanks!
8 Answers, 1 is accepted

When I try to create the grid using a function *within* a template nothing happens, the grid does not display. See "this fails".
However, when I call the function outside of the template, everything renders fine. See "this works".
My function works outside the template, but not inside the template itself. The code is:
<!--- Create the template that we will use to allow the description to be expanded. --->
<script type="text/x-kendo-template" id="resourceDetailTemplate">
<div class="tabstrip" style="width: 100%">
<ul>
<li class="k-state-active">
Active
</li>
<li>
Depracated
</li>
</ul>
<div>
<div>#= CategoryBody1 #</div>
</div>
<div>
#= CategoryBody2 #
</div>
</div>
<div id="subResourceGrid"></div>
<!--- (This fails) Instantiate the sub resource grid --->
#createSubResourceGrid(1);# //e.data.ResourceCategoryId
</script>
<script>
$(document).ready(function() {
// Preset vars
var expanded = {};
var grid, id;
// Create our resource category and sub category resources.
resourceCategoryDs = new kendo.data.DataSource({
// Determines which method and cfc to get and set data.
transport: {
read: {
url: "/common/cfc/Arehart.cfc?method=getResourceCategoryForGrid&pageTypeId=1", // the cfc component which processes the query and returns a json string.
dataType: "json", // Use json if the template is on the current server. If not, use jsonp for cross domain reads.
method: "post" // Note: when the method is set to "get", the query will be cached by default. This is not ideal.
},
parameterMap: function(options, operation) {
if (operation !== "read" && options.models) {
return {models: kendo.stringify(options.models)};
}
}
},
cache: false,
batch: true, // determines if changes will be send to the server individually or as batch. Note: the bacth arg must be in the datasource declaration, and not in the grid. Otherwise, a post to the cfc will not be made.
pageSize: <cfif session.isMobile>5<cfelse>15</cfif>, // The number of rows within a grid.
schema: {
model: {
id: "ResourceCategoryId", // Note: in editiable grids- the id MUST be put in here, otherwise you will get a cryptic error 'Unable to get value of the property 'data': object is null or undefined'
fields: {
ResourcePageTypeId: { type: "number", editable: false, nullable: false },
ResourceTypeId: { type: "number", editable: false, nullable: false },
ResourceType: { type: "string", editable: false, nullable: false },
ResourceCategoryId: { type: "number", editable: false, nullable: false },
Category: { type: "string", editable: false, nullable: false },
CategoryBody1: { type: "string", editable: false, nullable: false },
CategoryBody2: { type: "string", editable: false, nullable: false },
Active: { type: "boolean", editable: false, nullable: false }
}//fields:
}//model:
},//schema
// Group by the resource type and category fields
group: [
{ field: "ResourceType" }
]
});//resourceDs = new kendo.data.DataSource({
// Grid declaration
$("#resourceGrid").kendoGrid({
dataSource: resourceCategoryDs,
// Edit arguments
editable: false,
// Header
headerTemplate: 'resourceHeaderTemplate',
// Toolbars. You can customize each button like the excel button below. The importExcel button is a custom button, and we need to wire it up to a custom handler below.
toolbar: ["search"],
search: {
// Which fields should we search for?
fields:
["Category", "CategoryBody1", "CategoryBody2"],
},
// General grid elements.
height: <cfif session.isMobile>425<cfelse>725</cfif>,
navigatable: true,
filterable: true,
sortable: {
mode: "multiple",
allowUnsort: true,
showIndexes: true
},
pageable: {
pageSizes: [15],
refresh: true,
numeric: true
},
groupable: false,
selectable: false,
<cfif session.isMobile>mobile: true,</cfif>
allowCopy: true,
reorderable: true,
resizable: true,
columnMenu: true,
detailTemplate: kendo.template($("#resourceDetailTemplate").html()),
detailInit: detailInit,
//dataBound: function() {
//this.expandRow(this.tbody.find("tr.k-master-row").first());
//},
columns: [{
// hidden columns (these columns must be in the grid in order to be searchable).
field:"CategoryBody1",
hidden: true,
// Columns
field:"CategoryBody2",
hidden: true,
}, {
field: "Category",
title: "Type",
filterable: true
}],// columns:
});// $("#resourceGrid").kendoGrid({
});
// Initialize the detail row.
function detailInit(e) {
var detailRow = e.detailRow;
detailRow.find(".tabstrip").kendoTabStrip();
// This works
//createSubResourceGrid(e.data.ResourceCategoryId);
}
// Function to instantiate the sub resource grid
function createSubResourceGrid(resourceId){
// Initialize the sub category grid within the kendo template
$("#subResourceGrid").kendoGrid({
dataSource: {
// Determines which method and cfc to get and set data.
transport: {
read: {
url: "/common/cfc/Arehart.cfc?method=getResourceSubCategoryForGrid&pageTypeId=1&resourceCategoryId=" + resourceId, // the cfc component which processes the query and returns a json string.
dataType: "json", // Use json if the template is on the current server. If not, use jsonp for cross domain reads.
method: "post" // Note: when the method is set to "get", the query will be cached by default. This is not ideal.
},
parameterMap: function(options, operation) {
if (operation !== "read" && options.models) {
return {models: kendo.stringify(options.models)};
}
}
}, // transport
serverPaging: true,
serverSorting: true,
serverFiltering: true,
pageSize: <cfif session.isMobile>5<cfelse>15</cfif>, // The number of rows within a grid.
schema: {
model: {
id: "ResourceSubCategoryId", // Note: in editiable grids- the id MUST be put in here, otherwise you will get a cryptic error 'Unable to get value of the property 'data': object is null or undefined'
fields: {
ResourceCategoryId: { type: "number", editable: false, nullable: false },
SubCategory: { type: "string", editable: false, nullable: false },
SubCategoryBody1: { type: "string", editable: false, nullable: false },
SubCategoryBody2: { type: "string", editable: false, nullable: false },
SubCategoryActive: { type: "boolean", editable: false, nullable: false }
}//fields:
}//model:
},//schema
}, //datasource
scrollable: false,
sortable: true,
pageable: true,
columns: [{
// Columns
field: "SubCategory",
title: "Category",
filterable: true
}],// columns:
});
} // function createSubResourceGrid
</script>

I found a solution- I need to use conditional logic in the detailInit method instead of trying to create the sub grid widget in the template. I suspect that you can't instantiate a Kendo widget using a javascript function inside a detail row template. The following code works:
Outer grid:
$(document).ready(function() {
// Preset vars
var expanded = {};
var grid, id;
// Create our resource category and sub category resources.
resourceCategoryDs = new kendo.data.DataSource({
// Determines which method and cfc to get and set data.
transport: {
read: {
url: "/common/cfc/Arehart.cfc?method=getResourceCategoryForGrid&pageTypeId=1", // the cfc component which processes the query and returns a json string.
dataType: "json", // Use json if the template is on the current server. If not, use jsonp for cross domain reads.
method: "post" // Note: when the method is set to "get", the query will be cached by default. This is not ideal.
},
parameterMap: function(options, operation) {
if (operation !== "read" && options.models) {
return {models: kendo.stringify(options.models)};
}
}
},
cache: false,
batch: true, // determines if changes will be send to the server individually or as batch. Note: the bacth arg must be in the datasource declaration, and not in the grid. Otherwise, a post to the cfc will not be made.
pageSize: <cfif session.isMobile>5<cfelse>15</cfif>, // The number of rows within a grid.
schema: {
model: {
id: "ResourceCategoryId", // Note: in editiable grids- the id MUST be put in here, otherwise you will get a cryptic error 'Unable to get value of the property 'data': object is null or undefined'
fields: {
ResourcePageTypeId: { type: "number", editable: false, nullable: false },
ResourceTypeId: { type: "number", editable: false, nullable: false },
ResourceType: { Category: "string", editable: false, nullable: false },
ResourceCategoryId: { type: "number", editable: false, nullable: false },
Category: { type: "string", editable: false, nullable: false },
CategoryBody1: { type: "string", editable: false, nullable: false },
CategoryBody2: { type: "string", editable: false, nullable: false },
ResourceSubCategoryId: { type: "number", editable: false, nullable: false },
Active: { type: "number", editable: false, nullable: false },
SubCategoryActive: { type: "number", editable: false, nullable: false }
}//fields:
}//model:
},//schema
// Group by the resource type and category fields
group: [
{ field: "ResourceType" }
]
});//resourceDs = new kendo.data.DataSource({
// Grid declaration
$("#resourceGrid").kendoGrid({
dataSource: resourceCategoryDs,
// Edit arguments
editable: false,
// Header
headerTemplate: 'resourceHeaderTemplate',
// Toolbars. You can customize each button like the excel button below. The importExcel button is a custom button, and we need to wire it up to a custom handler below.
toolbar: ["search"],
search: {
// Which fields should we search for?
fields:
["Category", "CategoryBody1", "CategoryBody2"],
},
// General grid elements.
height: <cfif session.isMobile>425<cfelse>725</cfif>,
navigatable: true,
filterable: true,
sortable: {
mode: "multiple",
allowUnsort: true,
showIndexes: true
},
pageable: {
pageSizes: [15],
refresh: true,
numeric: true
},
groupable: false,
selectable: false,
<cfif session.isMobile>mobile: true,</cfif>
allowCopy: true,
reorderable: true,
resizable: true,
columnMenu: true,
// The detail template is a Kendo template that is above. It will be displayed when the user clicks on the right arrow menu.
detailTemplate: kendo.template($("#resourceDetailTemplate").html()),
// The detailInit function (underneath this code block) is necessary to instantiate widgets on the detail kendo template above.
detailInit: detailInit,
//dataBound: function() {
//this.expandRow(this.tbody.find("tr.k-master-row").first());
//},
columns: [{
// hidden columns (these columns must be in the grid in order to be searchable).
field:"CategoryBody1",
hidden: true,
// Columns
field:"CategoryBody2",
hidden: true,
}, {
field: "Category",
title: "Category",
filterable: true
}],// columns:
});// $("#resourceGrid").kendoGrid({
});
// Initialize the detail row.
function detailInit(e) {
// Get the selected row in the grid
var detailRow = e.detailRow;
// instantiate the tabstrib
detailRow.find(".tabstrip").kendoTabStrip();
// Get the selected category and subCategoryId from the grid.
var categoryId = e.data.ResourceCategoryId;
var maxSubCategoryId = e.data.ResourceSubCategoryId;
// If the maxSubCategoryId is not null, instantiate the subcategory grid within the detail template.
if (maxSubCategoryId !== null){
createSubResourceGrid(categoryId);
}
}
// Function to instantiate the sub resource grid
function createSubResourceGrid(resourceId){
// Initialize the sub category grid within the kendo template
$("#subResourceGrid").kendoGrid({
dataSource: {
// Determines which method and cfc to get and set data.
transport: {
read: {
url: "/common/cfc/Arehart.cfc?method=getResourceSubCategoryForGrid&pageTypeId=1&resourceCategoryId=" + resourceId, // the cfc component which processes the query and returns a json string.
dataType: "json", // Use json if the template is on the current server. If not, use jsonp for cross domain reads.
method: "post" // Note: when the method is set to "get", the query will be cached by default. This is not ideal.
},
parameterMap: function(options, operation) {
if (operation !== "read" && options.models) {
return {models: kendo.stringify(options.models)};
}
}
}, // transport
serverPaging: true,
serverSorting: true,
serverFiltering: true,
pageSize: <cfif session.isMobile>5<cfelse>15</cfif>, // The number of rows within a grid.
schema: {
model: {
id: "ResourceSubCategoryId", // Note: in editiable grids- the id MUST be put in here, otherwise you will get a cryptic error 'Unable to get value of the property 'data': object is null or undefined'
fields: {
ResourceCategoryId: { type: "number", editable: false, nullable: false },
SubCategory: { type: "string", editable: false, nullable: false },
SubCategoryBody1: { type: "string", editable: false, nullable: false },
SubCategoryBody2: { type: "string", editable: false, nullable: false },
SubCategoryActive: { type: "boolean", editable: false, nullable: false }
}//fields:
}//model:
},//schema
}, //datasource
groupable: false,
selectable: false,
<cfif session.isMobile>mobile: true,</cfif>
allowCopy: true,
reorderable: true,
resizable: true,
columnMenu: true,
// The sub detail template is a Kendo template that is above. It will be displayed when the user clicks on the right arrow menu in this subcategory grid.
detailTemplate: kendo.template($("#resourceSubDetailTemplate").html()),
// The detailInit function (underneath this code block) is necessary to instantiate widgets on the detail kendo template above.
detailInit: detailSubInit,
columns: [{
// Columns
field: "SubCategory",
title: "Category",
filterable: true
}],// columns:
});
} // function createSubResourceGrid
Hi Gregory,
It is correct that if the scenario requires a master-detail relation, the grid's hierarchy functionality the recommended approach to undertake.
However, it is possible to create Kendo UI widgets within the RowTemplate. However, those widgets should be initialized as late as the DataBound event. Within the DataBound event handler, loop through the rows of the grid and manually initialize the widget.
The latter approach, however, might have a negative impact on the performance of the application. This is due to the fact that after every major operation the widgets should be reinitialized. With the hierarchy approach, the detail rows are initialized on-demand.
Regards,
Tsvetomir
Progress Telerik
Our thoughts here at Progress are with those affected by the outbreak.


Hi Gregory,
If you would like to add an additional parameter to the child grid that will be the filter from the parent grid, make use of the data option for the transport of the data source and pass the filter:
Alternatively, binding the two grids to a single data source is possible as shown in the example below:
https://demos.telerik.com/kendo-ui/datasource/shared-datasource
Let me know if additional assistance is needed.
Regards,
Tsvetomir
Progress Telerik
Our thoughts here at Progress are with those affected by the outbreak.

Thank-you for the response. I'll research them.
I found an interesting potential issue with mobile grids. When you use a detail row with detail init, the sub (or inner grid) cannot use mobile: true in the grid. Otherwise it will not render. It took me 4 or 5 hours to figure out why the mobile UI did not work and finally got to the mobile argument.
Hi Gregory,
Based on the provided information, I performed several tests utilizing the mobile option for both of the grids - the master and detail grid and it appears that they get rendered as expected. Is it possible for you to provide a sample in which the defect could be observed?
Meanwhile, examine the console of the browser for any potential JavaScript exceptions that might be thrown.
Looking forward to your reply.
Kind regards,
Tsvetomir
Progress Telerik
Our thoughts here at Progress are with those affected by the outbreak.