Saturday, January 24, 2015

I Recently used the JSTree plugin for the first time.  It has it's own API and it has quite a bit of functionality. 

In my experience, it is often not well-documented or easy to follow.
I decided to outline some highlights of how to use the plugin, areas that might not be well documented, discoveries that I made, and potential issues/bugs.

Hopefully, these might save time for others.
This is somewhat of a tutorial (not comprehensive)

Resources
JSTree's Home Page: http://www.jstree.com/
There is a corresponding GitHub site with alot of the Readme Information: https://github.com/vakata/jstree
You'll likely need to alternate between the two.

PART I.

Create Tree

You can create the JSTreea few different ways.  A couple examples:
 1. Create an initial HTML Structure, then call the jsTree function:
  <div id="myDiv">
   <ul>
    <li><a>Hello Thar!</a>
     <ul>
      <li><a>Good Day!</a></li>
     </ul>
    </li>
   </ul>
  </div>

 $(document).ready(function () {
  $("#myDiv").jstree();
 });

  Note: I first attempted to use multiple ajax calls to populate the html from a data source.  In my case, not all of the ajax calls returned before the .jstree() was called and not all of the content was converted from an unordered list into the tree.  Be careful with this.

 2. A better more practical way to create the tree: leave an empty <div> placeholder, and create your structure via the API:
  <div id="myDiv">
  </div>

  $(document).ready(function () {
   $("#myDiv").jstree();

   //Create Root Node and Child Node to match section "1" HTML structure
   var rootNodeRet = $("#myDiv").jstree('create_node', '#', { 'id': '#j1_1', 'text': 'Hello Thar!' }, 'last');
   $("#myDiv").jstree('create_node', rootNodeRet, { 'id': '#j1_2', 'text': 'Good Day!' }, 'last');
  });

  (I'll cover the API a bit later)

Plugins/Types:

 You can change or modify the base JSTree functionality by passing a JSON Object during the initial .jstree() call. 
 This includes built-in plugins, such as types or sorting.
 Plugins with examples are listed on the JSTree Site: http://www.jstree.com/plugins/

      Plugins Example:
 From the JSTree HomePage:
    $("#myDiv").jstree({
            "core" : {
      "check_callback" : true
       },
      "plugins" : [ "dnd" ]
    });

 In this example, the "dnd" plugin allows drag and drop functionality.  In the UI, You should be able to drag a node and place it at any level in the tree and it will automatically move there.
 The 'core' and 'check_callback' nodes of the json object are required for this particular plugin to work.

      Types Plugin:

  The Types Plugin is noteable.  There is a Themes plugin, with built-in themes that modify the Look and Feel.  I will not cover Themes, but Types are conceptually similar to a "custom theme."
  You can apply a type to a specific node, or the entire Tree Structure.
//Example:
      $("#myDiv").jstree({
            "core" : {
                "check_callback": true,
            },
            "types": {
                    "tree": {
                        "icon": "http://jstree.com/tree-icon.png"
                    },
                    "lightning": {
                        "icon": "glyphicon glyphicon-ok"
                    }
    },
     "plugins" : [ "types" ]
   });

  Both the "Tree" and "lightning" types can be set to a node with the following syntax:
  //  This only updates the root node icon/type
  $("#myDiv").jstree("set_type", "#j1_1", "tree");

  You can also override the default type to apply this to all of the nodes:
   $("#myDiv").jstree({
            "core" : {
                "check_callback": true,
            },
            "types": {
                    "default": {
                        "icon": "http://jstree.com/tree-icon.png"
                    }
    },
     "plugins" : [ "types" ]
   });

Interaction/API

      1. Node Attributes:
You can update the following common attributes (not comprehensive list) for each individual node in the tree:
 id         - set_id
 text      - set_text
 theme  - set_theme
 icon     - set_icon
 type     - set_type

 //An example of setting the node text to "Hi Thar!"
 //In this case of setting the text, pass #j1_1, which is the id of the node we are changing the text for
    $("#myDiv").jstree('set_text', '#j1_1', 'Hi Thar!');

  If you want to do a get, you need to use the same Syntax above:
   //get
    $("#myDiv").jstree('get_text', '#j1_1'); //'Hi Thar!'
    $("#myDiv").jstree('get_theme', '#j1_1'); //'default'

      2. Node Selection
If you want to perform the action of "selecting" a node in the Tree, there are multiple ways of accomplishing this.
Examples listed by the JSTree homepage:
 a. $('#myDiv').jstree(true).select_node('child_node_1');
 b. $('#myDiv').jstree('select_node', 'child_node_1');
 c. $.jstree.reference('#myDiv').select_node('child_node_1');
 "b" is probably the most current/common approach.

Strangely/Unfortunately, All this really does is selects the node in the UI, similar to clicking a node with the mouse.
 As criticism, in my opinion, the documentation concerning API isn't overtly clear concerning the differences between:
  * Getting a node object for updating properties/attributes
  * Selecting the node as a selected/clicked node in the UI

 You might assume that you can pass the selected node as an object to the set_(xyz) functions as a parameter, but this doesn't work:
   //Not the correct syntax
   var myRootNode = $('#myDiv').jstree('select_node', '#j1_1');
   $("#myDiv").jstree('set_text', myRootNode, 'Hi Thar!');

  Instead, you need to use the syntax indicated in Step 1 with the hardcoded string of the ID:
   //Correct syntax
   $("#myDiv").jstree('set_text', '#j1_1', 'Hi Thar!');

  You also cannot get the node ID From a select_node to pass into the set_text function, partially because there is no get_id function in the API
  (it would be redundant to do this anyway, because you used the ID to get the select_node object to begin with).

 After Selecting a node, you can deselect them in the UI with the following approaches:
  Deselect all nodes in the UI (select multiple nodes in UI with ctrl+ mouseclick):
   $('#myDiv').jstree('deselect_all');
  Deselect specific node based on the ID:
   $('#myDiv').jstree('deselect_node', '#j1_2');

3. Creation
You can create a node with the following syntax:
 $("#jstree").jstree('create_node', parentNodeObject, value, 'last');

The child node syntax (the 'value' parameter above) needs to be a JSON Object of a format similar to the following:
       {
         id : "string",
         text : "string",
         icon : "string",
         state :
         {
            opened : boolean
            disabled : boolean
            selected  : boolean
         },
        children : []
       }
 None of the nodes/options in this object are required.
 (Full information on node structure on JSTree site: http://www.jstree.com/docs/json/)


Like Setting Attributes, you can't create a node by using the 'select_node' function, then passing it as a parameter to the create_node function.
 //Wrong Syntax:
  //select parent node using 'select_node'
   var rootNodeRet = $('#myDiv').jstree('select_node', '#j1_1');

  //New node JSON Object
   var newNodeJs = { 'id': '#j1_1', 'text': 'Hello Thar!' };

  //Create Node under parent
   $("#myDiv").jstree('create_node', rootNodeRet, newNodeJs, 'last');

You can, thankfully, create a node with the hard coded id of the parent node.
 //Correct Syntax (using hardcoded ID[string]):
  //New node JSON Object
   var newNodeJs = { 'id': '#j1_1', 'text': 'Hello Thar!' };

  //Create Node
   var rootNodeRet = $("#myDiv").jstree('create_node', '#j1_1', newNodeJs, 'last');

For Creating a Root Node, use the "#" sign instead of a reference to a Parent Node:
 //Root Node Json Object
  var newNodeJs = { 'id': '#j1_1', 'text': 'Hello Thar!' };

    //Root Node Create Call passing newNodeJs
    var rootNodeRet = $("#myDiv").jstree('create_node', '#', newNodeJs, 'last');

Additionally, a useful approach might be to store your new nodes in var objects, such as the following:
After your initial Creation of the root node, you can pass a reference to the parent node object:
 // 1. Create the root node
  var rootNodeJsObject = { 'id': '#j1_1', 'text': 'Hello Thar!' };
  var rootNodeRet = $("#myDiv").jstree('create_node', '#', rootNodeJsObject, 'last');

 // 2. Create the child node with reference to Root Node Object you created (rootNodeRet)
  var firstChildNodeRet = $("#myDiv").jstree('create_node', rootNodeRet, { 'id': '#j1_2', 'text': 'Good Day!' }, 'last');

 // 3. Use firstChildNodeRet to add a third-level child inside the second level:
  $("#myDiv").jstree('create_node', firstChildNodeRet, { 'id': '#j1_3', 'text': 'Happy Festivus!' }, 'last');

  (In 2 and 3: The Node will be added regardless of whether you associate the function with a 'var' object or not)
 I spent a good amount of time troubleshooting why select_node wouldn't work as a parameter until a colleague/mentor discovered this for me.
 It might be helpful if this was documented more clearly on the JSTree site.