Last active
August 29, 2015 14:09
-
-
Save smonn/bf5e2bf80de37347d9b5 to your computer and use it in GitHub Desktop.
knockout.js binding handler for http://www.datatables.net
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*global jQuery: false, ko: false */ | |
(function ($, ko) { | |
'use strict'; | |
// based on the work by http://chadmullins.com/ | |
// some caveats to be aware of: | |
// - x-editables: when clicking a sortable column the editable-unsaved css | |
// class gets removed since each row is re-rendered | |
// - data-bind on <tr/> is not supported (yet) | |
// - avoid data-bind on thead, tbody, etc, it won't work | |
ko.bindingHandlers.dataTable = { | |
// used for keeping track of table initialization | |
initializedKey: '__ko_bindingHandlers_dataTable_initialized', | |
// used for caching a row | |
rowIndexKey: '__ko_bindingHandler_dataTable_rowIndexKey', | |
// init method... | |
init: function (element, valueAccessor) { | |
var initializedKey = ko.bindingHandlers.dataTable.initializedKey, | |
rowIndexKey = ko.bindingHandlers.dataTable.rowIndexKey, | |
result = { | |
controlsDescendantBindings: true | |
}, | |
rawData = [], | |
oldRowCallback, | |
column, | |
options, | |
dataSource, | |
dataTable, | |
il, | |
i; | |
// already initialized | |
if ($.data(element, initializedKey) === true) { | |
return result; | |
} | |
// get the data table configuration | |
options = ko.utils.unwrapObservable(valueAccessor()); | |
if (!options.columns) { | |
throw 'Must provide the columns option.'; | |
} | |
// convert the source data observable into a simple array | |
// and handle changes to the source data | |
if (options.data && ko.isObservable(options.data) && $.type(options.data()) === 'array') { | |
dataSource = options.data; | |
options.data = dataSource(); | |
// cannot use subscribeArrayChanged since the source observable | |
// might be a computed observable | |
dataSource.subscribe(function (prevValue) { | |
// store the previous value for later comparison | |
rawData = prevValue.slice(0); | |
}, undefined, 'beforeChange'); | |
dataSource.subscribe(function (newValue) { | |
var diff = ko.utils.compareArrays(rawData, newValue), | |
data, | |
row; | |
// remove/add rows according to the changes | |
for (i = 0, il = diff.length; i < il; i += 1) { | |
data = diff[i]; | |
switch (data.status) { | |
case 'deleted': | |
row = data.value[rowIndexKey] && data.value[rowIndexKey].row; | |
if (row) { | |
dataTable.row(row).remove().draw(); | |
} | |
break; | |
case 'added': | |
dataTable.row.add(data.value).draw(); | |
break; | |
} | |
} | |
}); | |
} else { | |
throw 'The data source must be an observable and evaluate to an array.'; | |
} | |
// setup rendering with the required row callback | |
if (options.rowTemplate) { | |
oldRowCallback = options.rowCallback; | |
options.rowCallback = function (row, data) { | |
var updateCell = function (columnIndex) { | |
return function () { | |
var rowIndex = dataTable.row(row).index(); | |
dataTable.cell(rowIndex, columnIndex).invalidate(); | |
}; | |
}; | |
if (typeof oldRowCallback === 'function') { | |
// usually not required to provide a custom callback | |
// for ko bindings since they're handled by this | |
// binding handler internally | |
oldRowCallback.apply(this, Array.prototype.slice.call(arguments)); | |
} | |
// overwrite the row content with our own template | |
ko.renderTemplate(options.rowTemplate, data, null, row, 'replaceChildren'); | |
// in order to keep the datatable data in sync with the | |
// source data, we must invalidate the related cell data as | |
// it gets updated, we do this on a cell-by-cell basis | |
for (i = 0, il = options.columns.length; i < il; i += 1) { | |
column = options.columns[i]; | |
if (column.data && data[column.data] && ko.isObservable(data[column.data])) { | |
data[column.data].subscribe(updateCell(i)); | |
} | |
} | |
// cache to row for later use | |
data[rowIndexKey] = function () { | |
return; | |
}; | |
data[rowIndexKey].row = row; | |
return row; | |
}; | |
} else { | |
throw 'No row template was provided.'; | |
} | |
// initialize the DataTable! | |
dataTable = $(element).DataTable(options); | |
$.data(element, initializedKey, true); | |
return result; | |
} | |
}; | |
}(jQuery, ko)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- include knockout.js and the dataTable binding handler for this to work --> | |
<table data-bind="dataTable: { data: people, columns: [ { data: 'name' }, { data: 'age' } ], rowTemplate: 'row-template' }"> | |
<thead> | |
<tr> | |
<th>Name</th> | |
<th>Age</th> | |
</tr> | |
</thead> | |
</table> | |
<script id="row-template" type="text/html"> | |
<td><span data-bind="text: name"></span></td> | |
<td><span data-bind="text: age"></span></td> | |
</script> | |
<script> | |
var viewModel = { | |
// source data must be an observable! | |
people: ko.observableArray([ | |
{ name: 'Bob', age: 45 }, | |
{ name: 'Albert', age: 39 } | |
]) | |
}; | |
ko.applyBindings(viewModel); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment