This is a migrated thread and some comments may be shown as answers.

Grid within template

8 Answers 398 Views
Grid
This is a migrated thread and some comments may be shown as answers.
gregory
Top achievements
Rank 1
gregory asked on 20 Mar 2020, 03:40 AM

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

Sort by
0
gregory
Top achievements
Rank 1
answered on 21 Mar 2020, 02:21 AM

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>

 

0
gregory
Top achievements
Rank 1
answered on 22 Mar 2020, 02:02 PM

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

0
Accepted
Tsvetomir
Telerik team
answered on 23 Mar 2020, 12:54 PM

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

Progress is here for your business, like always. Read more about the measures we are taking to ensure business continuity and help fight the COVID-19 pandemic.
Our thoughts here at Progress are with those affected by the outbreak.
0
gregory
Top achievements
Rank 1
answered on 17 May 2020, 09:14 PM
Thanks Tsvetomir. How would you filter the nested grid when a filter is placed on the outer grid? I have two grids, the outer category grid, and the inner subcategory grid. When I filter the category grid using the filter methods I can't seem to figure out a way to also pass in an individual subcategory.
0
gregory
Top achievements
Rank 1
answered on 17 May 2020, 10:47 PM
Even better would be an example of using 1 datasource for both the outer and inner grids. There would be redundant data for the outer grid (a category can have zero, one, or many subcategories). Is this even possible? That would allow me to skip getting data for each subCategory from the server and would eliminate the necessity to figure out a way to filter when the inner subcategory grid is initialized using a function. 
0
Accepted
Tsvetomir
Telerik team
answered on 19 May 2020, 02:50 PM

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:

https://docs.telerik.com/kendo-ui/api/javascript/data/datasource/configuration/transport.read#transportreaddata

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

Progress is here for your business, like always. Read more about the measures we are taking to ensure business continuity and help fight the COVID-19 pandemic.
Our thoughts here at Progress are with those affected by the outbreak.
0
gregory
Top achievements
Rank 1
answered on 21 May 2020, 07:05 PM

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. 

0
Tsvetomir
Telerik team
answered on 25 May 2020, 11:11 AM

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

Progress is here for your business, like always. Read more about the measures we are taking to ensure business continuity and help fight the COVID-19 pandemic.
Our thoughts here at Progress are with those affected by the outbreak.
Tags
Grid
Asked by
gregory
Top achievements
Rank 1
Answers by
gregory
Top achievements
Rank 1
Tsvetomir
Telerik team
Share this question
or