简体   繁体   English

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

[英]Datatable data binding using knockoutjs

I want to render data into table using datatable knockoutjs binding. 我想使用datatable knockoutjs绑定将数据呈现到表中。 I am using the below link and code for rendering data into table. 我使用以下链接和代码将数据呈现到表中。 http://datatables.net/dev/knockout/ http://datatables.net/dev/knockout/

The only change I did in above example is while rendering age data I have added input box in age col for ever record and Updatebutton at the bottom of table, so that user can change his age and click of update button data should be updated automatically and in next page it should reflect in table. 我在上面的示例中做的唯一更改是在渲染年龄数据时,我在age col中添加了输入框以供记录,而在表底部添加Updatebutton,以便用户可以更改其年龄并单击更新按钮数据应自动更新在下一页中,它应该反映在表格中。

The issue am facing is that i am unable to update local js "people" model and hence unable to bind updated data using knockoutjs. 我面临的问题是我无法更新本地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 = '<div style="display:inline-flex">' + 
                                                '<input type="text" class="headerStyle h5Style" id="ageId" value="'+data()+'"/>' +
                                                '</div>';

                                  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
    );



} );

This is the way to do it... I have made a jsfiddle showing this: 这是做到这一点的方式......我已经做了一个jsfiddle显示:

Edit: Recently worked out a way to get this binding using vanilla knockout. 编辑:最近找到了一种使用vanilla knockout获得此绑定的方法。 I've tested this out on the latest version of knockout (3.4) Just use this binding and knockout datatables works! 我已经在最新版本的淘汰赛中测试了这个(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 的jsfiddle

I made a fiddle with a solution 我做了一个解决方案

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

Clicking "Update" will display the current knockout model as text below the button. 单击“更新”将当前的挖空模型显示为按钮下方的文本。

What was missing was the linking the change of the textbox to the observable by adding a listener in the render function. 缺少的是通过在render函数中添加一个监听器来链接文本框到observable的更改。 Also each row's textbox was being given the same id, which is not a good idea either. 此外,每一行的文本框都被赋予相同的ID,这也不是一个好主意。 (Note: the event aliases are just to prevent collision with other handlers) (注意:事件别名只是为了防止与其他处理程序发生冲突)

Changing the render function to build useful ids and adding the following should work: 更改渲染函数以构建有用的ID并添加以下内容应该有效:

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

Ideally Knockout would handle this for you but since you are not calling applyBindings nor creating the data-bind attributes for the html elements all that knockout really gives you here is the observable pattern. 理想情况下,Knockout会为你处理这个问题但是因为你没有调用applyBindings,也没有为html元素创建数据绑定属性,所有敲门声真正给你的是可观察模式。

Edit: Additional Solution 编辑:其他解决方案

Looking into it a little bit more you can let Knockout handle the rendering by adding the data-bind attribute into the template and binding your knockout model to the table element. 进一步研究一下,你可以让Knockout通过将data-bind属性添加到模板中并将你的挖空模型绑定到table元素来处理渲染。

var html = '<div style="display:inline-flex">' + 
    '<input type="text" class="headerStyle h5Style" id="' + id + '" data-bind="value: $data[' + cell.row + '].age"/>'

And

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

This removes the whole custom subscription call when constructing the Person object as well. 这也在构造Person对象时删除整个自定义订阅调用。

Here is another fiddle with the 2nd solution: 这是第二个解决方案的另一个小提琴:

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

I feel like this simplifies the solution. 我觉得这样可以简化解决方案。 However, i do not know how efficient it performs nor have i tested it with paging so additional work may need to be done. 但是,我不知道它的执行效率,也没有用分页测试它,所以可能需要做额外的工作。 With this method the mRender function is never re-executed and the DOM manipulation for the input is done entirely with knockout. 使用此方法,永远不会重新执行mRender函数,并且输入的DOM操作完全使用knockout完成。

Here is a simple workaround that re-binds the data in knockout and then destroys/recreates the datatable: 这是一个简单的解决方法,它可以重新绑定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/ https://jsfiddle.net/benjblack/xty5y9ng/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM