Search code examples
javascriptknockout.jsknockout-2.0nested-sortableknockout-sortable

KnockoutJS: Checking if items in nested sortable were reordered


The following is a tree displayed as nestedSortable using template in KnockoutJS.

So far I have a tree that works, but I was not able to find an efficient way to check for the changes caused by mouse dragging.

The solution should allow checking for change events whenever a node is dragged (tracking the changes is necessary for saving or updating the data). It should be able to determine what was changed in the tree.

The attributes of interest would be id of the node moved, id of new (and original) parent.

How to properly monitor the mouse events, such as mouse drag with this type of sortable?

function Node(data) {
  var self = this;
  typeof data != 'undefined' ? self.id = data.id : self.id = '1';
  self.parent = ko.observable();
  self.children = ko.observableArray();
  self.addNode = function() {
    var child = new Node({
      'id': self.id + '.' + (self.children().length + 1)
    });
    child.parent(self);
    self.children.push(child);
    return child;
  }
};
var tree = new Node();
var child1 = tree.addNode();
var child11 = child1.addNode();
var child12 = child1.addNode();
var child2 = tree.addNode();
var child21 = child2.addNode();
var child3 = tree.addNode();
var viewModel = function() {
  this.tree = ko.observable(tree);
};
ko.applyBindings(new viewModel());

$('.sortable')
  .nestedSortable({
    startCollapsed: false
  });
ol.sortable,
ol.sortable ol {
  margin: 0 0 0 25px;
  padding: 0;
  list-style-type: none;
}
ol.sortable {
  margin: 4em 0;
}
.sortable li {
  margin: 5px 0 0 0;
  padding: 0;
}
.sortable li div {
  border: 1px solid #d4d4d4;
  cursor: move;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.js"></script>
<script src="https://cdn.rawgit.com/furf/jquery-ui-touch-punch/master/jquery.ui.touch-punch.min.js"></script>
<script src="https://cdn.rawgit.com/mjsarfatti/nestedSortable/master/jquery.mjs.nestedSortable.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<div class="dd" data-bind="template: {name:'nodeTemplate', data: tree}"></div>
<script id='nodeTemplate' type='text/html'>
  <div data-bind="text: id"></div>
  </div>
  <ol class="sortable ui-sortable" data-bind="foreach: { data: children, as: 'child' }">
    <li data-bind="template: {name:'nodeTemplate', data: child}, attr: { 'data-id': child.id }">
    </li>
  </ol>
</script>


Solution

  • It's just a direction. You can use events of the "parent" Sortable Widget.

    May be the "stop" event of the Sortable Widget helps you?

    var size = 5;
    var navigationItems = {};
    navigationItems['id'] = 'list_' + (i + 1);
    navigationItems['displayLabel'] = 'list_' + (i + 1);
    navigationItems['children'] = new Array(size);
    for (var i = 0; i < size; i++) {
      navigationItems['children'][i] = {};
      navigationItems['children'][i]['id'] = 'list_' + (i + 1) + ".1";
      navigationItems['children'][i]['displayLabel'] = 'list_' + (i + 1) + ".1";
      navigationItems['children'][i]['children'] = new Array(1);
      navigationItems['children'][i]['children'][0] = {};
      navigationItems['children'][i]['children'][0]['id'] = 'list_' + (i + 1) + ".1.1";
      navigationItems['children'][i]['children'][0]['displayLabel'] = 'list_' + (i + 1) + ".1.1";
      navigationItems['children'][i]['children'][0]['children'] = new Array(0);
    };
    var viewModel = function() {
      this.tree = ko.observable();
    };
    var viewModelInstance = new viewModel();
    viewModelInstance.tree(navigationItems);
    ko.applyBindings(viewModelInstance);
    
    var stopHandler = function(args) {
        var model = ko.dataFor(args.target);
        alert(model.id);
    };
    $('.sortable')
      .nestedSortable({
        startCollapsed: false,
        stop: stopHandler
      });
    ol.sortable,
    ol.sortable ol {
      margin: 0 0 0 25px;
      padding: 0;
      list-style-type: none;
    }
    ol.sortable {
      margin: 4em 0;
    }
    .sortable li {
      margin: 5px 0 0 0;
      padding: 0;
    }
    .sortable li div {
      border: 1px solid #d4d4d4;
      cursor: move;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.js"></script>
    <script src="https://cdn.rawgit.com/furf/jquery-ui-touch-punch/master/jquery.ui.touch-punch.min.js"></script>
    <script src="https://cdn.rawgit.com/mjsarfatti/nestedSortable/master/jquery.mjs.nestedSortable.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
    
    <div class="dd" data-bind="template: {name:'nodeTemplate', data: tree}"></div>
    <script id='nodeTemplate' type='text/html'>
      <div data-bind="text: displayLabel"></div>
      </div>
      <ol class="sortable ui-sortable" data-bind="foreach: { data: children, as: 'child' }">
        <!-- ko if: child.children.length > 0 -->
        <li class="mjs-nestedSortable-expanded mjs-nestedSortable-branch" data-bind="template: {name:'nodeTemplate', data: child}, attr: { 'data-id': child.id }">
        </li>
        <!-- /ko -->
        <!-- ko if: child.children.length == 0 -->
        <li class="mjs-nestedSortable-leaf" data-bind="template: {name:'nodeTemplate', data: child}, attr: { 'data-id': child.id }">
        </li>
        <!-- /ko -->
      </ol>
    </script>