Search code examples
jqueryhtmljquery-uicakephpjstree

Is it possible to drag and drop external data into a jstree?


I'm working on a project which requires a hierarchical navigation menu to be built. jstree looks good for this.

The tree will be saved to a database - I am planning on using CakePHP's Tree Behaviour (the project has to work in Cake 2.x rather than 3.x due to the existing codebase).

One of the things I need to do is have the ability to add "Tags" to my tree from an external data source.

The way I have things configured is as follows:

The data to populate my jstree is coming from a database table (called navigations due to Cake's naming conventions). It uses the table structure given on the Tree Behaviour link above.

I'm loading this data into a jstree with the ajax method:

$.ajax({
    type : "GET",
    url : "/ajax_get_tree",
    dataType : "json",    

    success : function(json) {
        createJSTrees(json);
    },    

    error : function(xhr, ajaxOptions, thrownError) {
        alert(xhr.status);
        alert(thrownError);
    }
});


function createJSTrees(jsonData) {
    $("#tree").jstree({
        'core': {
            "check_callback" : true,
            'data' : jsonData
        },
        "plugins" : ["dnd"]
    }).on('loaded.jstree', function() {
        $("#tree").jstree('open_all');
    });
} 

What I want to do is drag and drop "Tags" (by which I mean list items) from a separate div, #tagList into elements on the tree. The tag data is formatted as follows:

<div id="tagList">
    <li data-tag="1">United Kingdom</li>
    <li data-tag="2">France</li>
    <li data-tag="3">Germany</li>
</div>

I know it's possible to use things like jqueryui's draggable behaviour to move them from one div to another (from #tagList into #tree).

However, I don't know how I can "drop" the tag such that jstree lists it under the appropriate node.

For example I've tried this - which is just using jqueryui and has nothing to do with jstree other than referencing the div it's running on:

$('#tagList li').draggable({
        cursor: 'move',
        helper: 'clone',
        connectToSortable: "#tree",
    });

My question really is whether what I'm trying to do is even possible? I've spent a long time looking into this and feel I'm getting nowhere with it.

Are there any other things out there that can do this type of task? I've had a look but was unable to find anything. Essentially the requirements are the ability to create a tree (name, edit, delete, drag/drop) but then to also bring Tags (<li> elements from the #tagList) into it, and save it.


Solution

  • Based on the articles I posted in the comments, you need to create an object that jsTree will be familiar with.

    Reference:

    Working Example:

    https://jsfiddle.net/Twisty/dLv7xk3t/

    HTML

    <div class="ui-widget">
      <div class="ui-widget-header">
        Tags
      </div>
      <div id="tagList">
        <ul>
          <li data-tag="1" id="uk-1">United Kingdom</li>
          <li data-tag="2" id="france-1">France</li>
          <li data-tag="3" id="germany-1">Germany</li>
        </ul>
      </div>
    </div>
    <div class="ui-widget">
      <div class="ui-widget-header">
        Tree
      </div>
      <div id="tree">
      </div>
    </div>
    

    JavaScript

    var exData = [{
      id: "loc1",
      parent: "#",
      text: "Location 1"
    }, {
      id: "loc2",
      parent: "#",
      text: "Location 2"
    }, {
      id: "italy-1",
      parent: "loc2",
      text: "Italy",
      icon: "fa fa-flag"
    }, {
      id: "poland-1",
      parent: "loc2",
      text: "Poland",
      icon: "fa fa-flag"
    }];
    
    function makeTreeItem(el) {
      return $("<a>", {
        id: $(el).attr("id") + "_anchor",
        class: "jstree-anchor",
        href: "#"
      });
    }
    
    $(function() {
      $('#tree').jstree({
        core: {
          check_callback: true,
          data: exData
        },
        types: {
          root: {
            icon: "fa fa-globe-o"
          }
        },
        plugins: ["dnd", "types"]
      });
      $('#tagList li').draggable({
        cursor: 'move',
        helper: 'clone',
        start: function(e, ui) {
          var item = $("<div>", {
            id: "jstree-dnd",
            class: "jstree-default"
          });
          $("<i>", {
            class: "jstree-icon jstree-er"
          }).appendTo(item);
          item.append($(this).text());
          var idRoot = $(this).attr("id").slice(0, -2);
          var newId = idRoot + "-" + ($("#tree [id|='" + idRoot + "'][class*='jstree-node']").length + 1);
          return $.vakata.dnd.start(e, {
            jstree: true,
            obj: makeTreeItem(this),
            nodes: [{
              id: newId,
              text: $(this).text(),
              icon: "fa fa-flag-o"
            }]
          }, item);
        }
      });
    });
    

    The function makeTreeItem() is simply a wrapping function to make the item dragged in more like what jsTree already has.

    The first thing to do is update the core preference to enable the creation of new nodes etc:

    check_callback: true

    The next key here is in the draggable start callback. This is where we create a Drag n Drop element that jsTree is already ready to handle, making use of the dnd plugin for jsTree.

      start: function(e, ui) {
          var item = $("<div>", {
            id: "jstree-dnd",
            class: "jstree-default"
          });
          $("<i>", {
            class: "jstree-icon jstree-er"
          }).appendTo(item);
          item.append($(this).text());
          var idRoot = $(this).attr("id").slice(0, -2);
          var newId = idRoot + "-" + ($("#tree [id|='" + idRoot + "'][class*='jstree-node']").length + 1);
          return $.vakata.dnd.start(e, {
            jstree: true,
            obj: makeTreeItem(this),
            nodes: [{
              id: newId,
              text: $(this).text(),
              icon: "fa fa-flag-o"
            }]
          }, item);
        }
    

    Basically, we have a div with an icon and the text of the item being dragged. This will represent our helper that will be seen while dragging. We then make and return an Event Object with specific attributes that jsTree will understand and our helper item.