手动html修改后,observableArray.remove无法正常工作

时间:2016-12-05 08:04:37

标签: knockout.js

示例代码在这里。 https://jsfiddle.net/zLnuyk3w/4/

function ViewModel(){
    var self = this;
  var ids = [
    { id:50, order:1},
    { id:25, order:2},
    { id:35, order:3}  
  ];
  var list = ko.observableArray(ids);
  self.sortedList = ko.observableArray();
  ko.computed(function(){
    var computedList = list().sort(function(l, r){
        return l.order < r.order ? -1 : 1;
    });
    self.sortedList(computedList);
  });  
  self.addNewItem = function(){
    list.push({id: 40, order:2});
  }
  self.deleteItem = function(item){
    list.remove(item);
  }      
}
var vm = new ViewModel();
ko.applyBindings(vm);

$('tbody').sortable({
 update: function(event, ui) {
  // vm.list().updateOrder();   // somehow update 'order' property but for simplicity reason, omit the actual code
  vm.list.valueHasMutated();
 }
});
$( "tbody" ).disableSelection();

我基本上想用jquery UI可排序插件手工排序一行表。但在手工排序并尝试从observableArray中删除项目后,删除无法正常工作。

我彻底搜索相关问题,似乎我可能需要在调用ko.cleanNode后重新绑定视图模型。这是解决这个问题的唯一方法吗?我实际上想要避免这种方法,因为我正在玩的数据是动态生成的,并且视图使用了很多模板,因此为重新绑定元素传递正确的数据非常复杂。

2 个答案:

答案 0 :(得分:2)

问题比你想象的要简单,而不是淘汰相关:

对于具有相同0索引的项目,您不会返回order。将排序方法更改为:

return l.order === r.order ? 0 : l.order < r.order ? -1 : 1;

现在,在添加项目时,您的表格至少可以正确排序。如果要通过某种外部逻辑动态更改order属性,则必须使它们observable。你会离开这个:

self.sortedList = ko.pureComputed(function() {
    return list().sort(function(l, r){
        return l.order === r.order 
          ? 0 
          : l.order < r.order ? -1 : 1;
    });
});

对此:

self.sortedList = ko.pureComputed(function() {
    return list().sort(function(l, r){
        var oL = l.order(), 
            oR = r.order();
        return oL === oR
          ? 0 
          : oL < oR ? -1 : 1;
    });
});

现在,只要list或其中一个项order属性发生更改,列表就会重新排序。

如果这不起作用,或者不是你需要的,问题可能在jQuery小部件中。如果它从您的html中删除元素,则淘汰赛可能会失去其连接。 (例如,如果按克隆元素排序并重新排序)

您可以通过编写在update函数中重新应用窗口小部件的自定义绑定处理程序,或者通过删除插件并使用我正在描述的系统来解决此问题。看看这个例子:

function ViewModel() {
  var self = this;
  var ids = [{
    id: 50,
    order: ko.observable(1)
  }, {
    id: 25,
    order: ko.observable(2)
  }, {
    id: 35,
    order: ko.observable(3)
  }];
  var list = ko.observableArray(ids);
  self.sortedList = ko.observableArray();
  ko.computed(function() {
    var computedList = list().sort(function(l, r) {
      var lO = l.order(),
        rO = r.order();
      return lO === rO ? 0 : lO < rO ? -1 : 1;
    });
    self.sortedList(computedList);
  });
  self.addNewItem = function() {
    list.push({
      id: 40,
      order: 0
    });
  }
  self.deleteItem = function(item) {
    list.remove(item);
  }

}
var vm = new ViewModel();
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<table>
  <tbody data-bind="foreach: sortedList">
    <tr>
      <td data-bind="text:id"></td>
      <td>
        <label>order:
          <input type="number" data-bind="value: order">
        </label>
      </td>
    </tr>
  </tbody>
</table>

答案 1 :(得分:0)

正如托马拉克所说的那样,它比我想象的要复杂得多,他指出的自定义装订效果非常好 https://github.com/rniemeyer/knockout-sortable

我稍后会发布示例代码。

修改
我无法弄清楚如何将本地js文件添加到fiddler所以这次只是一个示例代码。请参考knockout-sortable.js,它会为您完成一项工作。在这种情况下,我不需要计算的observable,因为项目在列表中的位置实际上是由自定义绑定更改的,这就是我想要的。当然,如果您想要使用其他操作更改列表的顺序,则需要使用计算的observable进行排序。

<table>
<tbody data-bind="sortable: list" >
<tr>
  <td data-bind="text:id"></td>
  <td ><button data-bind="click:$root.deleteItem">
  delete
  </button></td>
</tr>
</tbody>
</table>

function ViewModel(){
    var self = this;
  var ids = [
    { id:50},
    { id:25},
    { id:35}  
  ];
  self.list = ko.observableArray(ids);
  self.deleteItem = function(item){
    self.list.remove(item);
  }
}    
ko.applyBindings(new ViewModel());