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.