二维敲除排序不更新UI

Two-dimensional knockout sortable not updating UI

我正在创建一个二维 sortable 容器,第一维(table 中的行)和第二维(一行中的单元格)。

单元格应可在一行内拖动到现有行,拖动到动态创建的新行。应动态删除空行。这些单元格配置为连续占据所有 space。

如何编辑自定义 Knockout 排序器table 绑定(例如 update 事件)?

之前:

之后:

更新问题:

//connect items with observableArrays
ko.bindingHandlers.sortableList = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    $(element).data("sortList", valueAccessor().data); //attach meta-data
    $(element).sortable({
      placeholder: valueAccessor().placeholder,
      start: function(event, ui) {},
      change: function(event, ui) {},
      update: function(event, ui) {
        var item = ui.item.data("sortItem");
        if (item) {
          //identify parents
          var originalParent = ui.item.data("parentList");
          var newParent = ui.item.parent().data("sortList");
          //identify viewModel
          var viewModel = bindingContext.$root;
          //figure out its new position
          var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);

          if (ui.item.parent()[0].classList.contains("sortable-row")) {
            //Row already exists
            console.log("true");
          } else {
            //Row doesn't exist, create new row (PROBLEM WITH UPDATE HERE)
            newParent().splice(position, 0, {
              "children": ko.observableArray([])
            });
            newParent = newParent()[position].children;
          }

          //Update item position
          originalParent.remove(item);
          newParent.splice(position, 0, item);
          
          //Remove empty lists
          var children = viewModel.children();
          for (var i = 0; i < children.length; i++) {
            if (children[i].children().length == 0) {
              viewModel.children.remove(children[i]);
              console.log(children);
            }
          }

          //Update UI
          ui.item.remove();

          //Debug data model
          console.log("final viewModel");
          var children = viewModel.children();
          for (var i = 0; i < children.length; i++) {
            console.log(children[i].children());
            for (var j = 0; j < children[i].children().length; j++) { 
             console.log(children[i].children()[j].children(),children[i].children()[j].content());
            }
          }
        }

      },
      connectWith: '.sortable-container'
    });
  }
};
//attach meta-data
ko.bindingHandlers.sortableItem = {
  init: function(element, valueAccessor) {
    var options = valueAccessor();
    $(element).data("sortItem", options.item);
    $(element).data("parentList", options.parentList);
  }
};
var self = this;
var viewModel = function() {
  var self = this;
  self.children = ko.observableArray(
    [{
      "children": ko.observableArray([{
        "content": ko.observable("Item 1"),
        "children": ko.observableArray([])
      }, {
        "content": ko.observable("Item 2"),
        "children": ko.observableArray([])
      }, {
        "content": ko.observable("Item 3"),
        "children": ko.observableArray([])
      }])
    }, {
      "children": ko.observableArray([{
        "content": ko.observable("Item 4"),
        "children": ko.observableArray([])
      }])
    }, {
      "children": ko.observableArray([{
        "content": ko.observable("Item 5"),
        "children": ko.observableArray([])
      }, {
        "content": ko.observable("Item 6"),
        "children": ko.observableArray([])
      }])
    }]
  );
};
ko.applyBindings(new viewModel());
.sortable-table {
  border: 1px red solid;
  padding: 10px 0px;
  list-style-type: none;
  width: 100% !important;
  display: table !important;
}
.sortable-table .sortable-row {
  height: 100% !important;
  display: table-row !important;
  padding: 5px 0px;
}
.sortable-table .sortable-cell {
  border: 1px solid green;
  display: table-cell !important;
  cursor: move;
}
.sortable-table .sortable-cell p {
  display: inline;
  margin: 0 !important;
}
.sortable-table .highlight-vertical {
  width: 5px !important;
  display: table-cell !important;
  background-color: blue !important;
}
.sortable-table .highlight-horizontal {
  height: 5px !important;
  width: 100% !important;
  display: block !important;
  background-color: blue !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rniemeyer.github.com/KnockMeOut/Scripts/jquery.tmpl.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>

<div class="sortable-container" data-bind="template: { name: 'rowTmpl', foreach: $data.children, templateOptions: { parentList: $data.children } }, sortableList: { data: $data.children, placeholder: 'highlight-horizontal' }">
</div>

<script id="rowTmpl" type="text/html">
  <div class="sortable-table">
    <div class="sortable-row sortable-container" data-bind="template: { name: 'cellTmpl', foreach: $data.children, templateOptions: { parentList: $data.children } }, sortableList: { data: $data.children, placeholder: 'highlight-vertical' }">
    </div>
  </div>
</script>

<script id="cellTmpl" type="text/html">
  <div class="sortable-cell" data-bind="sortableItem: { item: $data, parentList: $item.parentList }">
    <p data-bind="text: $data.content"></p>
  </div>
</script>

问题出在线上 newParent.splice(position, 0, {"children": ko.observableArray([])});newParent 被称为 newParent(),这是导致问题的原因。

//connect items with observableArrays
ko.bindingHandlers.sortableList = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    $(element).data("sortList", valueAccessor().data); //attach meta-data
    $(element).sortable({
      placeholder: valueAccessor().placeholder,
      start: function(event, ui) {},
      change: function(event, ui) {},
      update: function(event, ui) {
        var item = ui.item.data("sortItem");
        if (item) {
          //identify parents
          var originalParent = ui.item.data("parentList");
          var newParent = ui.item.parent().data("sortList");
          //identify viewModel
          var viewModel = bindingContext.$root;
          //figure out its new position
          var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);

          if (ui.item.parent()[0].classList.contains("sortable-row")) {
            //Row already exists
            console.log("true");
          } else {
            //Row doesn't exist, create new row (PROBLEM WITH UPDATE HERE)
            newParent.splice(position, 0, {
              "children": ko.observableArray([])
            });
            newParent = newParent()[position].children;
          }

          //Update item position
          originalParent.remove(item);
          newParent.splice(position, 0, item);
          
          //Remove empty lists
          var children = viewModel.children();
          for (var i = 0; i < children.length; i++) {
            if (children[i].children().length == 0) {
              viewModel.children.remove(children[i]);
              console.log(children);
            }
          }

          //Update UI
          ui.item.remove();

          //Debug data model
          console.log("final viewModel");
          var children = viewModel.children();
          for (var i = 0; i < children.length; i++) {
            console.log(children[i].children());
            for (var j = 0; j < children[i].children().length; j++) { 
             console.log(children[i].children()[j].children(),children[i].children()[j].content());
            }
          }
        }

      },
      connectWith: '.sortable-container'
    });
  }
};
//attach meta-data
ko.bindingHandlers.sortableItem = {
  init: function(element, valueAccessor) {
    var options = valueAccessor();
    $(element).data("sortItem", options.item);
    $(element).data("parentList", options.parentList);
  }
};
var self = this;
var viewModel = function() {
  var self = this;
  self.children = ko.observableArray(
    [{
      "children": ko.observableArray([{
        "content": ko.observable("Item 1"),
        "children": ko.observableArray([])
      }, {
        "content": ko.observable("Item 2"),
        "children": ko.observableArray([])
      }, {
        "content": ko.observable("Item 3"),
        "children": ko.observableArray([])
      }])
    }, {
      "children": ko.observableArray([{
        "content": ko.observable("Item 4"),
        "children": ko.observableArray([])
      }])
    }, {
      "children": ko.observableArray([{
        "content": ko.observable("Item 5"),
        "children": ko.observableArray([])
      }, {
        "content": ko.observable("Item 6"),
        "children": ko.observableArray([])
      }])
    }]
  );
};
ko.applyBindings(new viewModel());
.sortable-table {
  border: 1px red solid;
  padding: 10px 0px;
  list-style-type: none;
  width: 100% !important;
  display: table !important;
}
.sortable-table .sortable-row {
  height: 100% !important;
  display: table-row !important;
  padding: 5px 0px;
}
.sortable-table .sortable-cell {
  border: 1px solid green;
  display: table-cell !important;
  cursor: move;
}
.sortable-table .sortable-cell p {
  display: inline;
  margin: 0 !important;
}
.sortable-table .highlight-vertical {
  width: 5px !important;
  display: table-cell !important;
  background-color: blue !important;
}
.sortable-table .highlight-horizontal {
  height: 5px !important;
  width: 100% !important;
  display: block !important;
  background-color: blue !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rniemeyer.github.com/KnockMeOut/Scripts/jquery.tmpl.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>

<div class="sortable-container" data-bind="template: { name: 'rowTmpl', foreach: $data.children, templateOptions: { parentList: $data.children } }, sortableList: { data: $data.children, placeholder: 'highlight-horizontal' }">
</div>

<script id="rowTmpl" type="text/html">
  <div class="sortable-table">
    <div class="sortable-row sortable-container" data-bind="template: { name: 'cellTmpl', foreach: $data.children, templateOptions: { parentList: $data.children } }, sortableList: { data: $data.children, placeholder: 'highlight-vertical' }">
    </div>
  </div>
</script>

<script id="cellTmpl" type="text/html">
  <div class="sortable-cell" data-bind="sortableItem: { item: $data, parentList: $item.parentList }">
    <p data-bind="text: $data.content"></p>
  </div>
</script>