Get the immediate parent element in target data on afterMove arg in a single observableArray

184 Views Asked by At

I have a single nested observable array on which I am applying sortable. I am dropping an item from inside one of the nested elements to inside another nested element.

How can I get the immediate parent node of the source and destination after item is moved.

If I move item no 11 to item no 16, after move I want to the source and destination parent nodes as 10 and 15.

  
  function itemAfterMove(args){
   console.log("move");
  }
 
  ko.bindingHandlers.typeahead = {
   init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {

    var $element = $(element);
    var allBindings = allBindingsAccessor();
    //var value = ko.utils.unwrapObservable(allBindings.value);
    var source = ko.utils.unwrapObservable(valueAccessor());
    var items = ko.utils.unwrapObservable(allBindings.items) || 4;

    var valueChange = function (item) {
     //console.log('item = ' + item);
     return item;
    };

    var highlighter = function (item) {
     var matchSpan = '<span style="color: blue;font-weight:bold">';
     var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
     return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
      return matchSpan + match + '</span>';
     });
    };

    var options = {
     source: source,
     items: items,
     updater: valueChange
    };

    $element
     .attr('autocomplete', 'off')
     .typeahead(options);
   }
  };

  var counter = 1;
  var fireCount = 1;

  var data = ['Iron Man', 'Iron Man 2', 'Iron Man 3', 'Avengers',
   'LOTR: The Fellowship of the Ring',
   'LOTR: The Two Towers',
   'LOTR: The Return of the Ring',
   'Star Wars I: Phantom Menace',
   'Star Wars II: Attack of the Clones',
   'Star Wars III: Revenge of the Sith',
   'Star Wars IV: A New Hope',
   'Star Wars V: The Empire Strikes Back',
   'Star Wars VI: Return of the Jedi',
   'The Princess Bride',
   'Thor',
   'Captain America'
  ];


  var movies = ko.observableArray(data);

  var ListItem = function (name, items = []) {

   return {

    isActive: ko.observable(false),
    name: ko.observable(name),
    items: ko.observableArray(items)
   }
  }

  var items = [  
   {  
      "isActive":false,
      "name":1,
      "items":[  
         {  
            "isActive":false,
            "name":7,
            "items":[  
               {  
                  "isActive":false,
                  "name":8,
                  "items":[  
                     {  
                        "isActive":false,
                        "name":15,
                        "items":[  
                           {  
                              "isActive":false,
                              "name":16,
                              "items":[  

                              ]
                           }
                        ]
                     },
                     {  
                        "isActive":false,
                        "name":9,
                        "items":[  

                        ]
                     }
                  ]
               }
            ]
         }
      ]
   },
   {  
      "isActive":false,
      "name":2,
      "items":[  

      ]
   },
   {  
      "isActive":false,
      "name":3,
      "items":[  
         {  
            "isActive":false,
            "name":10,
            "items":[  
               {  
                  "isActive":false,
                  "name":11,
                  "items":[  
                     {  
                        "isActive":false,
                        "name":12,
                        "items":[  

                        ]
                     }
                  ]
               }
            ]
         }
      ]
   },
   {  
      "isActive":false,
      "name":4,
      "items":[  

      ]
   },
   {  
      "isActive":false,
      "name":5,
      "items":[  
         {  
            "isActive":false,
            "name":6,
            "items":[  
               {  
                  "isActive":false,
                  "name":14,
                  "items":[  

                  ]
               }
            ]
         },
         {  
            "isActive":false,
            "name":13,
            "items":[  

            ]
         }
      ]
   }
];
  items.push(new ListItem(counter++, []));
  items.push(new ListItem(counter++, []));
  items.push(new ListItem(counter++, []));
  items.push(new ListItem(counter++, []));
  items.push(new ListItem(counter++, [new ListItem(counter++, [])]));

  function MyViewModel() {
   var self = this;

   self.movies = movies;

   self.items = ko.observableArray(items);

   self.addChild = function (data) {
    //console.log("fired" + fireCount++);

    data.items.push(new ListItem(counter++, []));
   }

   self.addSibling = function (data, parent) {
    //console.log("fired" + fireCount++);

    data.items.push(new ListItem(counter++, []));
   }

   self.showStructure = function () {

    console.log(ko.toJS(self.items));
   }
  }

  $(document).ready(function () {
   ko.applyBindings(new MyViewModel());
  });
<html>

<head>
 <style type="text/css">
  .drag {
   display: inline-block;
   width: 20px;
   height: 20px;
   background-color: #e1e1e1;
   cursor: move;
  }

  .node {
   margin-bottom: 10px;
  }
 </style>
 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.2/css/bootstrap.css">
 <script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
 <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery-sortable/0.9.13/jquery-sortable-min.js"></script>
 <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.js"></script>
 <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.2/js/bootstrap.js"></script>


 <script type="text/javascript" src="http://knockoutjs.com/downloads/knockout-3.3.0.js"></script>
 <script type="text/javascript" src="https://rawgithub.com/rniemeyer/knockout-sortable/master/build/knockout-sortable.js"></script>
</head>

<body>
 <h3>Instructions</h3>

 <p>Drop <em>poo</em> out of baz</p>

 <button data-bind="click:showStructure">Console log Structure(latest)</button>
 <div data-bind="template: 'tree'"></div>

 <script type="text/htm" id="tree">
  <ul class="tree" data-bind="sortable : {
    data        : items,
    options     : {
     placeholder : 'drop-placeholder',
     delay       : 100,
     handle      : '.drag',
     scroll      : true
    }, afterMove: itemAfterMove
   }">
   <li class="node">
    <button data-bind="click:$root.addChild,clickBubble:false">Add Child</button>
    <button data-bind="click:$root.addSibling.bind($data,$parent),clickBubble:false">Add Sibling</button>
    <input type="text" data-bind="typeahead: $root.movies, value: name, items: 3" />
    <span class="drag"></span>
    <span data-bind="text: name"></span>
    <div data-bind="template: {name: 'tree', data: $data}"></div>

   </li>
  </ul>  
 </script>


</body>

1

There are 1 best solutions below

4
Brother Woodrow On

This information should be readily available to you in the afterMove function. According to the docs:

This function receives an object for its first argument that contains the following information:

arg.item - the actual item being moved
arg.sourceIndex - the position of the item in the original observableArray
arg.sourceParent - the original observableArray
arg.sourceParentNode - the container node of the original list. Useful if moving items between lists, but within a single array. The value of this in the callback will be the target container node.
arg.targetIndex - the position of the item in the destination observableArray
arg.targetParent - the destination observableArray