-
Notifications
You must be signed in to change notification settings - Fork 1
/
knockout-datatables.js
72 lines (56 loc) · 3.28 KB
/
knockout-datatables.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
ko.bindingHandlers.datatable = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
//KO doesn't have this inbuilt so copied from a knockout contributers suggestion https://github.com/knockout/knockout/pull/706
function isObservableArray(value) { return ko.isObservable(value) && 'push' in value; }
var options = ko.toJS(valueAccessor());
//datatables errors aren't very descriptive, so I'll help where I can
if (!isObservableArray(valueAccessor().data)) throw "Knockout-Datatables requires the input to be a Knockout observableArray"
if (!options.uniqueKey) throw "Knockout-Datatables requires the uniqueKey input that accesses a unique peoperty in each object";
if (!options.data) throw "Knockout-Datatables requires a datasource.";
if (options.ajax) throw "Knockout-Datatables does not handle ajax. Please retreive and persist data externally.";
$(element).empty(); //datatables will build the table dynamically
var uniqueKey = options.uniqueKey, keyToVMMap = { };
options.columns.unshift({ data: uniqueKey, visible: false });//add a hidden column to find the row using the uniqueKey
valueAccessor().data().forEach(function(data) {
keyToVMMap[ko.toJS(data[uniqueKey])] = data;//store VM's to access once rows are created;
});
var createdRow = options.createdRow;//store user inputted function to replace with our own function
function rebindChildren(row, data, index) {
var vm = keyToVMMap[data[uniqueKey]];
ko.applyBindingsToDescendants(bindingContext.createChildContext(vm), row);
ko.computed(function() { return ko.toJS(vm); })//allows us to subscribe to the knockout object
.subscribe(function() {
dtAPI.row(row).data(ko.toJS(vm)).invalidate().draw(); //update row data and invalidate existing data
ko.cleanNode(row); //TODO: investigate more - the row is still bound to ko but the bindings don't work correctly - so rebind for now
ko.applyBindingsToDescendants(bindingContext.createChildContext(vm), row);
});
if (createdRow) createdRow(row, data, index); //call user inputted function
}
options.createdRow = rebindChildren;
var dtAPI = $(element).DataTable(options);
var dataAdded = function(data) {
keyToVMMap[ko.toJS(data[uniqueKey])] = data;
dtAPI.row.add(ko.toJS(data)).draw();
};
var dataRemoved = function(data) {
var key = ko.toJS(data)[uniqueKey];
var rowIndex = dtAPI.column(0).data().indexOf(ko.toJS(data)[uniqueKey]);
//if a datatable is re-sorted rowLoop is assigned the new index and the index stays the same
dtAPI.rows().every(function(index, tableLoop, rowLoop) {
if (rowIndex == rowLoop) dtAPI.row(index).remove().draw();
});
};
var oldData;
var fnBeforeChange = function(dataBeforeChange) { oldData = dataBeforeChange.slice(0); };//deep copy to prevent updating oldData
var fnAfterChange = function(dataAfterChange) {
var differences = ko.utils.compareArrays(oldData, dataAfterChange);
differences.forEach(function(difference) {
if (difference.status === 'added') dataAdded(difference.value);
if (difference.status === 'deleted') dataRemoved(difference.value);
});
};
valueAccessor().data.subscribe(fnBeforeChange, null, 'beforeChange');
valueAccessor().data.subscribe(fnAfterChange);
return { controlsDescendantBindings: true };
}
}