使用jQuery数据表的KNockoutJS,绑定行未正确更新

时间:2013-10-19 19:02:19

标签: javascript knockout.js jquery-datatables

这是JS代码:

function ProductViewModel() {
  // Init.
  var self = this;
  self.products = ko.observableArray();
  self.singleProduct = ko.observable();
  var mappedProducts;

  // Initialize table here.
  $.getJSON("/admin/test", function(allData) {
    mappedProducts = $.map(allData, function(item) { return new Product(item);});
    self.products(mappedProducts);

    self.oTable = $('#products-table').dataTable( {
      "aoColumns": [
        { "bSortable": false, "mDataProp": null, sDefaultContent: '' },
        {"mData": "name"}, 
        {"mData": "dealer"},
        {"mData": "cost"},
        {"mData": "price"}, 
        { "bSortable": false, sDefaultContent: '' }
      ],
    });
  });

  // Here i'm using the basic switch pattern, as from KO tutorials.
  // This is intended for showing a single product form.
  self.edit = function(product) {
    self.singleProduct(product);
  }

  // This is intended to hide form and show list back.
  self.list = function() {
    self.singleProduct(null);
  }

  // This is the form save handler, actually does nothing 
  // but switch the view back on list.
  self.doEdit = function(product) {
    self.list();
  } 
}



// My model.
function Product(item) {
  this.name = ko.observable(item.name);
  this.dealer = ko.observable(item.dealer);
  this.cost = ko.observable(item.cost);
  this.price = ko.observable(item.price);
  this.picture = ko.observable();
}

这是我的标记:

<table id="products-table" class="table table-striped table-bordered table-hover">
        <thead>
          <tr>
            <th>Pic</th>
            <th>Name</th>
            <th>Dealer</th>
            <th>Cost</th>
            <th>Price</th>
            <th>Actions</th>
          </tr>
        </thead>

        <tbody data-bind="foreach: $parent.products">
          <tr>
            <td><span data-bind='ifnot: picture'>-</span></td>
            <td><a data-bind="text: name"></a></td>
            <td><span data-bind='text: dealer'></span></td>
            <td><span data-bind='text: cost'></span></td>
            <td><span data-bind='text: price'></span></td>
            <td>
              <button data-bind='click: $root.edit'><i class='icon-pencil'></i>
              </button>
            </td>
          </tr>
        </tbody>
      </table>

当我点击编辑按钮,触发$ root.edit处理程序时,会显示一个表单,因为

<div data-bind='with: singleProduct'>

绑定我做了。在这个绑定中我有一个表单,带有

<input data-bind="value: name" type="text" id="" placeholder="Name"
> class="col-xs-10 col-sm-5">

字段。

问题:当我在输入字段中编辑值时,数据表中的相对行不会更新。我已经尝试了没有datatables插件的基本表,它确实有效,这意味着如果我更改了值,表中的行会正确更新。

这里有什么问题?

==编辑==

我发现将绑定点移动到表TD修复了问题,但仍然无法弄清楚原因。

          <tr>
            <td data-bind="text: name"></td>
            <!-- More columns... -->
          </tr>

上面的代码现在正常运行。为什么?

== EDIT2 ==

既然我已经解决了第一个问题,那就是第二个问题。我实现了我的“保存新”方法,如此

self.doAdd = function(product) {
    $.ajax("/product/", {
      data: ko.toJSON({ product: product }),
      type: "post", contentType: "application/json",
      success: function(result) { alert('ehh'); }
    }).then(function(){
      self.products.push(product); // <--- Look at this!
      self.list();
    });
  }

self.products.push(product);在成功处理程序中正确地更新我的产品observable。然后,一个新行会自动添加到我的表中,这是个好消息。

坏消息是,一旦我在阵列中推送新产品,数据表控件(如搜索字段或可点击排序箭头)就会消失。为什么这样!?

2 个答案:

答案 0 :(得分:1)

你有没有解决这个问题?

我多年来一直有类似的问题。

最后我的修复是使用ko.mapping.fromJS(实体)映射我的所有实体 - 然后连接所有必需的依赖项并确保任何更改都流过我的模型。

答案 1 :(得分:0)

http://jsfiddle.net/zachpainter77/4tLabu56/

ko.bindingHandlers.DataTablesForEach = {

        init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
             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, allBindingsAccessor, viewModel, bindingContext) {

            var value = ko.unwrap(valueAccessor()),
            key = "DataTablesForEach_Initialized";

            var newValue = function () {
                return {
                    data: value.data || value,
                    beforeRenderAll: function (el, index, data) {

                        if (ko.utils.domData.get(element, key)) {

                            $(element).closest('table').DataTable().destroy();
                        }
                    },
                    afterRenderAll: function (el, index, data) {
                        $(element).closest('table').DataTable(value.options);
                    }

                };
            };

            ko.bindingHandlers.foreach.update(element, newValue, allBindingsAccessor, viewModel, bindingContext);

            //if we have not previously marked this as initialized and there is currently items in the array, then cache on the element that it has been initialized
            if (!ko.utils.domData.get(element, key) && (value.data || value.length)) {
                ko.utils.domData.set(element, key, true);
            }

            return { controlsDescendantBindings: true };
        }
    };

https://rawgit.com/zachpainter77/zach-knockout.js/master/zach-knockout.debug.js