Reputation: 2176
Kendo UI supports detailTemplate
to be used, however how to use it via Knockout-kendo bindings?
The jsfiddle code is here
just adding rowTemplate
and detailTemplate
doesnt work, rowTemplate
shows up but when i open open the details i get all sorts of exception (object expected in ASP.NET and jsfiddle breaks)
Upvotes: 2
Views: 1548
Reputation: 3202
Ideally, this could be fixed by a pull request to kendo-knockout. In lieu of that, you can get knockout data to bind and render properly in a kendo grid detail template by:
First, add the detailTemplate to the list of templates for kendoGrid. To do this, open knockout-kendo.js, search for 'kendoGrid', then add "detailTemplate" to the array of template names. It should look like this after your change:
createBinding({
name: "kendoGrid",
defaultOption: DATA,
watch: {
data: function(value, options) {
ko.kendo.setDataSource(this, value, options);
}
},
templates: ["rowTemplate", "altRowTemplate", "detailTemplate"]
});
Second, in addition to having a detailTemplate defined for your binding, add a detailInit binding that binds to a method on your viewmodel. Here is a sample binding:
<div data-bind="kendoGrid: { data: items, detailTemplate: 'myKoTemplate',
useKOTemplates: true, detailInit: myDetailInit } "> </div>
Third, add the following detailInit method to your viewmodel, so that the binding can find it:
this.myDetailInit = function(e) {
// Manually fire the databound event on the grid to
// get the detail template to unmemoize properly
e.sender.options.dataBound();
}
Here's a pen with a working example of this: https://codepen.io/codethug/pen/MXoqZy
Still reading? Great. So what's going on here and why is this broken in the first place?
The first part is easy. detailTemplate isn't listed as a template for kendoGrid. Adding that in makes kendo knockout render the template. However, the template gets memoized, but it never gets unmemoized.
Memoization, in this context, means that when kendo asks for the template from the kendo-knockout template renderer, which in turn hands it off to the knockout template renderer, the template is not immediately rendered, but instead, a placeholder in the form of an HTML comment that looks like this: <!--[ko_memo:123abc]-->
is inserted in the DOM instead of the rendered template.
Knockout-Kendo makes the assumption that after the templates are rendered, that the dataBound event will be fired on the kendo grid widget. Knockout-kendo hooks into that event to them unmemoize the templates. For usage of templates like the rowTemplate, this assumption is true. When setting up the widget, Kendo calls the render method, then kendo calls the dataBound method.
However, with detailTemplate, the assumption is not true. When you click something to expand details on a kendo grid row, the renderTemplate method is called, but the dataBound event is not fired, presumably because the data didn't change.
We can get around this by manually firing the dataBound event, as seen in the code above. No data has actually changed, but firing that event will trigger kendo knockout into unmemoizing the HTML comment <!--[ko_memo:123abc]-->
and replacing it with the properly rendered template.
Upvotes: 0
Reputation: 2176
I followed a different approach where have two rows in row template itself and one row is actual data, the other is detail row. Then use the accordion to trigger the show/hide of detail row. used bootstrap for accordion... works well for me, though its round about way. btw internally this is how they do it as well, but dynamically.
Upvotes: 0
Reputation: 2718
Here is what I came up with (based on your explanation). I know you have already done a workaround, but here is an answer so this question can at least have an answer in case anyone else runs into this.
View
<div data-bind="kendoGrid: { data: items, columns: columns, pageable: { pageSize: 3 }, scrollable: false, rowTemplate: 'rowTmpl', useKOTemplates: true }"> </div>
<script id="rowTmpl" type="text/html">
<tr>
<td data-bind="click: $parent.toggleShowDetails">+</td>
<td data-bind="text: id"></td>
<td data-bind="text: name"></td>
</tr>
<tr data-bind="visible: showDetails">
<td colspan="3">
<div data-bind="kendoGrid: { data: $data.details, scrollable: false }"></div>
</td>
</tr>
</script>
ViewModel
var ViewModel = function () {
var self = this;
self.columns = [{ field: ' ' }, { field: 'id' }, { field: 'name'}];
self.items = ko.observableArray([
{ id: "1", name: "apple", details: [{ id: "1", name: "subApple"}], showDetails: ko.observable(false) },
{ id: "2", name: "orange", details: [{ id: "2", name: "subOrange"}], showDetails: ko.observable(false) },
{ id: "3", name: "banana", details: [{ id: "3", name: "subBanana"}], showDetails: ko.observable(false) },
{ id: "4", name: "pineapple", details: [{ id: "4", name: "subPineapple"}], showDetails: ko.observable(false) },
{ id: "5", name: "grape", details: [{ id: "5", name: "subGrape"}], showDetails: ko.observable(false) },
{ id: "6", name: "mango", details: [{ id: "6", name: "subMango"}], showDetails: ko.observable(false) }
]);
self.toggleShowDetails = function (data, event) {
data.showDetails(!data.showDetails());
};
};
ko.applyBindings(new ViewModel());
Link to GitHub Issues comment: https://github.com/kendo-labs/knockout-kendo/issues/75#issuecomment-20004008
Upvotes: 1