Search code examples
knockout.jsjstree

Jstree - extra classes added to the node getting lost upon collapsing


I am creating a permission based knockout JS jsTree where i pull in the tree data from an observableArray. I want to add/revoke permission to folder and sub-folder upon clicking as well as based on the JSON upon page load. But since the tree is re-rendering every single time upon collapsing, the added classes are getting lost. I have added an extra sprite image with a close icon to the existing sprite and change the sprite position upon clicking. ( Cannot post the screenshot because i dont have the minimum reputation to post images ). There can be deep nesting for the folders as well.

Current jsTree ( where X indicates the new folder icon )

-X Reports library
  - X Archives
- My Reports

/* Upon collapsing and expanding again: */

 -X Reports library
   - Archives
 - My Reports

The initial knockout js tree has been done using a variant of this fiddle

Upon page load, i need to set the icons based on the permissions(true/false) in JSON.I can add a permission:true attribute to the json as well. Also, i need to toggle a folder and that folder along with its sub-folder should toggle the folder icons indicating that the permission is revoked.

self.tree.on("changed.jstree", function (e, data) {
    var node = self.tree.jstree().get_selected(true)[0]; //get current selected node    

        var selId = "#"+ node.a_attr.id;
        $(selId).toggleClass("permission-icon");
        var nextItem = $(selId).next();
        var nextListItemFlag = $(selId).next().hasClass("jstree-children");

        if(nextListItemFlag){
            $(nextItem).children().each(function(){ 
                $(this).find(".jstree-icon.jstree-themeicon").toggleClass("custom-permission-icon");
            });             
        }

    if (typeof node !== 'undefined') {
        self.isNodeSelected(true);
        self.selectedNode(node);
        self.myinfo(node.original.folderdata);                      
        } else {
        self.isNodeSelected(false);
    }
});

self.treeData = ko.observableArray(
[{
    'id': 1,
    'parent': '#',
    'text': 'Reports Library',
    'state': {
        selected: false         
    },
    'type': '#',        
    'folderdata': [{name: "Reports", type: "folder", modified: "07-02-2015", size: "10 KB"},
                   {name: "New text doc", type: "text", modified: "07-02-2015", size: "20 KB"},
                   {name: "test.pptx", type: "pptx", modified: "07-02-2015", size: "39 KB"},
                   {name: "test.xlsx", type: "excel", modified: "07-02-2015", size: "50 KB"}
                  ],

    }, {
    'id': 2,
    'parent': '#',
    'text': 'Reports Archive',      
    'type' : '#',
    'folderdata': [{name: "Meetings", type: "folder", modified: "07-02-2015", size: "10 KB"},
                   {name: "logs", type: "text", modified: "07-02-2015", size: "20 KB"},
                   {name: "sessions", type: "pptx", modified: "07-02-2015", size: "39 KB"},
                   {name: "worklog", type: "excel", modified: "07-02-2015", size: "50 KB"}
                  ],
    }, {
    'id': 'dog',
    'parent': 1,
    'text': 'Reports',      
    'type' : 'folder'
}]

Any help will be greatly appreciated! Has been stuck on this thing for a day now :(


Solution

  • jsTree only keeps visible nodes in the DOM, so once a node is collapsed and then reopened - all its children are reloaded from the internal JSON representation - if you did not modify that, any changes you made to the DOM node are lost.

    Basically if you wanted to add a class, you'd do something like this (this is for the LI node, you can modify the A node as well of course):

    .on('changed.jstree', function (e, data) {
        // DOM
        data.instance.get_node(data.selected[0],true).addClass('newClass');    
        // internal model
        data.instance._model.data[data.selected[0]].li_attr.class += ' newClass';
    });
    

    The same goes for icons - there is a method to deal with icon changes - set_icon.