I\'m trying to execute callback on list of items to make pagination using DataTable. Now i want to execute my callback after all my items have been rendered not after each item
Here is a custom binding that I use! This covers any datatables options that you want to define in the binding...
ko.bindingHandlers.dataTablesForEach = {
page: 0,
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var options = ko.unwrap(valueAccessor());
ko.unwrap(options.data);
if(options.dataTableOptions.paging){
valueAccessor().data.subscribe(function (changes) {
var table = $(element).closest('table').DataTable();
ko.bindingHandlers.dataTablesForEach.page = table.page();
table.destroy();
}, null, 'arrayChange');
}
var nodes = Array.prototype.slice.call(element.childNodes, 0);
ko.utils.arrayForEach(nodes, function (node) {
if (node && node.nodeType !== 1) {
node.parentNode.removeChild(node);
}
});
return ko.bindingHandlers.foreach.init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
},
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var options = ko.unwrap(valueAccessor()),
key = 'DataTablesForEach_Initialized';
ko.unwrap(options.data);
var table;
if(!options.dataTableOptions.paging){
table = $(element).closest('table').DataTable();
table.destroy();
}
ko.bindingHandlers.foreach.update(element, valueAccessor, allBindings, viewModel, bindingContext);
table = $(element).closest('table').DataTable(options.dataTableOptions);
if (options.dataTableOptions.paging) {
if (table.page.info().pages - ko.bindingHandlers.dataTablesForEach.page == 0)
table.page(--ko.bindingHandlers.dataTablesForEach.page).draw(false);
else
table.page(ko.bindingHandlers.dataTablesForEach.page).draw(false);
}
if (!ko.utils.domData.get(element, key) && (options.data || options.length))
ko.utils.domData.set(element, key, true);
return { controlsDescendantBindings: true };
}
};
JSFIDDLE
From a logical point of view, shouldn't the ConvertToDataTable
binding be on the table itself, instead on the foreach
?
Also, shouldn't you control table layout via the binding or the view model? The custom binding is a very bad place for hard-coded values.
Anyway, controlsDescendantBindings
is your friend (docs):
Custom Binding:
ko.bindingHandlers.dataTable = {
init: function () {
return { controlsDescendantBindings: true };
},
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var layout = valueAccessor();
ko.applyBindingsToDescendants(bindingContext, element);
$(element).dataTable({ "sDom": layout });
}
};
View Model:
{
dataTableLayout: "<'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'span6'p>>",
tasks: ko.observableArray([/* ... */])
}
Template:
<table data-bind="dataTable: dataTableLayout">
<thead>
<tr>
<td>Task Name</td>
<td>Task Description</td>
</tr>
</thead>
<tbody data-bind="foreach: tasks">
<tr>
<td data-bind="text: Name"></td>
<td data-bind="text: Details"></td>
</tr>
</tbody>
</table>
http://jsfiddle.net/WcaM5/
Disclaimer: I don't know exactly how jQuery DataTables work, so the sample is aircode. The point I want to make is that you can take manual control over the binding if necessary.