16

I am trying to dynamically load the nodes of a jtree when they are expanded. The little documentation I found is at the end of this page.

I found some solutions that create the nodes one by one with a loop like this one. I have not tried it, but looking at the documentation page I have the feeling that jstree should take care of cycling through the nodes.

I found many solutions that use plugins: ["json_data"], but the plugins documentation page doesn't mention that plugin at all. Is that an old plugin that is not required anymore?

My current implementation uses this code to load the whole tree in one shot:

$.ajax({
    var pn = $('#project_number').val();
    url : "bomtree?part=" + pn,
    success : function(tree_content) {
        var data = $.parseJSON(tree_content);
        var config = {
            'core' : {
                'data' : data
            }
        };
        $('#bom_tree').jstree(config);
    }
});

I modified the code on the documentation page like this:

$(function() {
    var pn = $('#project_number').val();
    $('#tree').jstree({
        'core' : {
            'data' : {
                'url' : function(node) {
                    return '/doc/test2';
                },
                'data' : function(node) {
                    return {
                        'part' : node.id === '#' ? pn : node.id
                    };
                }
            }
        }
    });
});

The same json text works with the first code, now with the second. The documentation says The format remains the same as the above, so I didn't change it.

I tried also returning the same data as in the example, this:

[
       { "id" : "ajson1", "parent" : "#", "text" : "Simple root node" },
       { "id" : "ajson2", "parent" : "#", "text" : "Root node 2" },
       { "id" : "ajson3", "parent" : "ajson2", "text" : "Child 1" },
       { "id" : "ajson4", "parent" : "ajson2", "text" : "Child 2" },
]

But the result is the same: jquery throws a Sizzle.error at the following line:

Sizzle.error = function( msg ) {
    throw new Error( "Syntax error, unrecognized expression: " + msg );
};

Where the content of msg is the json data returned by the server.

What's wrong?

Community
  • 1
  • 1
stenci
  • 8,290
  • 14
  • 64
  • 104

5 Answers5

16

"When using AJAX set children to boolean true and jsTree will render the node as closed and make an additional request for that node when the user opens it.", this is from jstree document and it could achieve your requirement.

Nathan
  • 181
  • 1
  • 5
10

Extending Nathans answer with a (very minimalistic) example: the Demo tree with lazy loading.

JS:

$('#the_tree').jstree({
    'core' : {
        'data' : {
            'url' : "tree/ajax.php", 
              'data' : function (node) {
                  return { 'id' : node.id };
              }
        }
    },

});

PHP:

header('Content-Type: application/json');

if ( $_GET["id"] === "#" ) { 
    $data = array(
            array( "id" => "ajson1", "parent" => "#", "text" => "Simple root node"  ),
            array( "id" => "ajson2", "parent" => "#", "text" => "Root node 2", "children" => true ),

    );
}

else if ( $_GET["id"] === "ajson2" ) {
    $data = array(
        array( "id" => "ajson3", "parent" => "ajson2", "text" => "Child 1" ),
        array( "id" => "ajson4", "parent" => "ajson2", "text" => "Child 2" )
    );
}

echo json_encode( $data);

only Nodes that have "children" : true, will generate a request for children when opened, other nodes are treated as leaves.

cytofu
  • 873
  • 9
  • 17
8

I'll give you that the documentation/examples is pretty rough. I'll also add that the source of your confusion comes from a major upgrade - the old version doesn't have much in common with the new version and unfortunately most examples were written against that old version.

The good news is that lazy loading is supported out of the box, it just isn't that obvious. The key is that it does call your data: config as each node is expanded. But in order for it to work, the URL function must return a different URL for the given node. In the code below, note the condition to return one URL if the node is root (#), and another if it is not.

$('#TreeDiv')
  .jstree({
    core: {
      data: {
        url: function (node) {
          return node.id === '#' ? '/UserAccount/AccountGroupPermissions' 
                                 : '/UserAccount/AccountPermissions/' + node.id;
        },
        type: 'POST'
      }
   },
   plugins : ["checkbox"]
});
Chait
  • 1,052
  • 2
  • 18
  • 30
b_levitt
  • 7,059
  • 2
  • 41
  • 56
  • As of jsTree 3.3.5, you ~can~ use the same URL and still have lazy loading. Answer by André Bonna below reveals that setting `node.children = true` in the `'success'` property will cause the jsTree to utilize lazy loading and call the same URL each time a node is expanded. If only one URL is specified, it will be the URL used. – barrypicker Jan 27 '18 at 19:32
5

To make a lazy loading, you need a backend that returns a JSON object with tree nodes that has children property field. Children property must contain children elements or boolean true (array or boolean). With a strongly typed language on your backend it is going to be ugly, so its best to deal with it on frontend. Example of AJAX success callback:

$('#tree').jstree({
    'core' : {
        'data' : {
            'url' : function(node) {
                return '/doc/test2';
            },
            'data' : function(node) {
                return {
                    'part' : node.id === '#' ? pn : node.id
                };
            },
            'success' : function(nodes) {

                var validateChildrenArray = function(node) {

                    if (!node.children || node.children.length === 0) {
                        node.children = true;
                    }
                    else {
                        for (var i = 0; i < node.children.length; i++) {
                            validateChildrenArray(node.children[i]);
                        }
                    }
                };

                for (var i = 0; i < nodes.length; i++) {
                    validateChildrenArray(nodes[i]);
                }
            }
        }
    }
});

By doing this, you are going to be able to lazy load your tree.

André Bonna
  • 807
  • 8
  • 13
0

I made my customized lazy loading by combining "select_node.jstree" event and "create_node" method. On selecting every node, event handler checks if there are children and adds response of server to selected node, node by node. My server response was not similar or requirements of jstree and this strategy saved me a lot of time and effort. Hope it helps somebody.