KNockoutJS с таблицами данных jQuery, связанные строки не обновляются должным образом

Вот 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>

Приведенный выше код теперь работает правильно. Почему ?

== РЕДАКТИРОВАТЬ2 ==

Теперь, когда я исправил первую проблему, приходит вторая. Я реализовал свой метод «сохранить новый» так

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); в обработчике успеха правильно обновляет наблюдаемые продукты. Затем в мою таблицу автоматически добавляется новая строка, и это хорошая новость.

Плохая новость заключается в том, что элементы управления таблицами данных, такие как поле поиска или интерактивные стрелки сортировки, исчезают, как только я помещаю новый продукт в массив. Почему так!?


person brazorf    schedule 19.10.2013    source источник
comment
Ваша проблема в том, что ваш self.products() не обновляется с новым значением или что элемент в массиве mappedProducts не обновляется? Точно так же, как примечание mappedProducts не будет отражать изменение, потому что вы еще не привязаны к нему, вы привязываетесь к наблюдаемому массиву self.products. Вам нужно будет обновить массив mappedProducts вручную.   -  person PW Kad    schedule 20.10.2013
comment
KO Observables обновляются, но не внутри таблицы. В любом случае проверьте мое последнее редактирование, есть новости.   -  person brazorf    schedule 21.10.2013


Ответы (2)


Вы когда-нибудь решали это?

У меня были подобные проблемы в течение многих лет.

В конце концов, мое исправление заключалось в том, чтобы сопоставить все мои объекты с помощью ko.mapping.fromJS(entity) - это затем подключило все необходимые зависимости и гарантировало, что любые изменения будут проходить через мою модель.

person BrettH    schedule 13.12.2013
comment
Так и не исправил это, и разработчики DT на своих форумах сказали, что надо ждать стабильной версии DT 1.10, предыдущие релизы работать не будут. Кстати, можете ли вы опубликовать пример в своем ответе? - person brazorf; 13.12.2013

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

person Zach Painter    schedule 10.08.2015