使用knockoutjs进行数据表数据绑定

我想使用datatable knockoutjs绑定将数据呈现到表中。 我使用以下链接和代码将数据呈现到表中。 http://datatables.net/dev/knockout/

我在上面的示例中做的唯一更改是在渲染年龄数据时,我在age col中添加了输入框以供记录,而在表底部添加Updatebutton,以便用户可以更改其年龄并单击更新按钮数据应自动更新在下一页中,它应该反映在表格中。

我面临的问题是我无法更新本地js“人员”模型,因此无法使用knockoutjs绑定更新的数据。

ko.observableArray.fn.subscribeArrayChanged = function(addCallback, deleteCallback) { var previousValue = undefined; this.subscribe(function(_previousValue) { previousValue = _previousValue.slice(0); }, undefined, 'beforeChange'); this.subscribe(function(latestValue) { var editScript = ko.utils.compareArrays(previousValue, latestValue); for (var i = 0, j = editScript.length; i < j; i++) { switch (editScript[i].status) { case "retained": break; case "deleted": if (deleteCallback) deleteCallback(editScript[i].value); break; case "added": if (addCallback) addCallback(editScript[i].value); break; } } previousValue = undefined; }); };` `var data = [ { id: 0, first: "Allan", last: "Jardine", age: 86 }, { id: 1, first: "Bob", last: "Smith", age: 54 }, { id: 2, first: "Jimmy", last: "Jones", age: 32 } ]; ` `var Person = function(data, dt) { this.id = data.id; this.first = ko.observable(data.first); this.last = ko.observable(data.last); this.age = ko.observable(data.age); // Subscribe a listener to the observable properties for the table // and invalidate the DataTables row when they change so it will redraw var that = this; $.each( [ 'first', 'last', 'age' ], function (i, prop) { that[ prop ].subscribe( function (val) { // Find the row in the DataTable and invalidate it, which will // cause DataTables to re-read the data var rowIdx = dt.column( 0 ).data().indexOf( that.id ); dt.row( rowIdx ).invalidate(); } ); } ); }; $(document).ready(function() { var people = ko.mapping.fromJS( [] ); //loadData(); var dt = $('#example').DataTable( { "bPaginate": false, "bInfo" : false, "bAutoWidth" : false, "sDom" : 't', "columns": [ { "data": 'id' }, { "data": 'first' }, { "data": 'age', "mRender": function (data, type, row ) { var html = '
' + '' + '
'; return html; } } ] } ); // Update the table when the `people` array has items added or removed people.subscribeArrayChanged( function ( addedItem ) { dt.row.add( addedItem ).draw(); }, function ( deletedItem ) { var rowIdx = dt.column( 0 ).data().indexOf( deletedItem.id ); dt.row( rowIdx ).remove().draw(); } ); // Convert the data set into observable objects, and will also add the // initial data to the table ko.mapping.fromJS( data, { key: function(data) { var d = data; return ko.utils.unwrapObservable(d.id); }, create: function(options) { return new Person(options.data, dt); } }, people ); } );

我做了一个解决方案的小提琴

http://jsfiddle.net/Jarga/hg45z9rL/

单击“更新”将当前的挖空模型显示为按钮下方的文本。

缺少的是通过在render函数中添加一个监听器来链接文本框到observable的更改。 此外,每一行的文本框都被赋予相同的ID,这也不是一个好主意。 (注意:事件别名只是为了防止与其他处理程序冲突)

更改渲染函数以构建有用的ID并添加以下内容应该有效:

 $('#' + id).off('change.grid') $('#' + id).on('change.grid', function() { row.age($(this).val()); }); 

理想情况下Knockout会为你处理这个问题,但是既然你没有调用applyBindings,也没有为html元素创建数据绑定属性,那么淘汰赛真正给你的是可观察的模式。

编辑:其他解决方案

再看一下,你可以让Knockout通过将data-bind属性添加到模板中并将你的knockout模型绑定到table元素来处理渲染。

 var html = '
' + ''

 ko.applyBindings(people, document.getElementById("example")); 

这也会在构造Person对象时删除整个自定义订阅调用。

这是第二个解决方案的另一个小提琴:

http://jsfiddle.net/Jarga/a1gedjaa/

我觉得这样可以简化解决方案。 但是,我不知道它的执行效率,也没有用分页测试它,所以可能需要做额外的工作。 使用此方法,永远不会重新执行mRender函数,并且输入的DOM操作完全使用knockout完成。

这是这样做的方式……我已经做了一个jsfiddle显示:

编辑:最近找到了一种使用vanilla knockout获得此绑定的方法。 我已经在最新版本的淘汰赛中测试了这个(3.4)只需使用这个绑定和淘汰数据表就行了!

 ko.bindingHandlers.dataTablesForEach = { page: 0, init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { 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); // !!!!! Need to set dependency ko.bindingHandlers.foreach.update(element, valueAccessor, allBindings, viewModel, bindingContext); (function() { console.log(options); var 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

这是一个简单的解决方法,可以重新绑定knockout中的数据,然后销毁/重新创建数据表:

 // Here's my data model var ViewModel = function() { this.rows = ko.observable(null); this.datatableinstance = null; this.initArray = function() { var rowsource1 = [ { "firstName" : "John", "lastName" : "Doe", "age" : 23 }, { "firstName" : "Mary", "lastName" : "Smith", "age" : 32 } ]; this.redraw(rowsource1); } this.swapArray = function() { var rowsource2 = [ { "firstName" : "James", "lastName" : "Doe", "age" : 23 }, { "firstName" : "Alice", "lastName" : "Smith", "age" : 32 }, { "firstName" : "Doug", "lastName" : "Murphy", "age" : 40 } ]; this.redraw(rowsource2); } this.redraw = function(rowsource) { this.rows(rowsource); var options = { paging: false, "order": [[0, "desc"]], "searching":true }; var datatablescontainer = $('#datatablescontainer'); var html = $('#datatableshidden').html(); //Destroy datatable if (this.datatableinstance) { this.datatableinstance.destroy(); datatablescontainer.empty(); } //Recreate datatable datatablescontainer.html(html); this.datatableinstance = datatablescontainer.find('table.datatable').DataTable(options); } }; ko.applyBindings(new ViewModel("Planet", "Earth")); // This makes Knockout get to work 

https://jsfiddle.net/benjblack/xty5y9ng/