Search code examples
javascriptjquerytreedynatreejquery-dynatree

Dynatree - disable (or hide) the checkboxes from the children nodes when a node is selected


I am using dynatree to select multi-nodes in a hierarchy of directories.

So, I have investigated the behavior of all built-in options for selectMode, but, unfortunately, none of them resolves my problem:

  • 1: single-selection (not my case)
  • 2: multi-selection
    • it does not select the parent node when all children nodes are selected (ok)
    • it does not select all children nodes when the parent node is selected (not ok)
  • 3: hierarchical multi-selection
    • it selects the parent node when all children nodes are selected (not ok)
    • it selects all children nodes when the parent node is selected (ok)

The behavior for my case would be correct if:

  • it does not select the parent node when all children nodes are selected
    • selecting all children directories is different from selecting the parent of them
  • it selects all children nodes when the parent node is selected
    • otherwise, the user might not understand that the children are also selected

Question #1: How to implement this behavior?

Furthermore, the dtnode.tree.getSelectedNodes() is returning redundant nodes:

/Foo
/Foo/Bar

If /Foo is selected, it is already implied that /Foo/Bar is selected, for me.

Question #2: How to optimize the selected nodes redundancy?


Solution

  • The best solution that I see involves using selectMode: 2 and toggling the visibility of the children's checkboxes when selecting a node:

    //enable or disable redundant children nodes recursively
    function toggleChildrenNodes(disableChildren, dtnode) {
        if (!dtnode)
            return;
    
        if (!dtnode.childList)
            return;
    
        for (var chIdx = 0; chIdx < dtnode.childList.length; chIdx++) {
            dtnode.childList[chIdx].data.hideCheckbox = disableChildren;
            dtnode.childList[chIdx].render(true);
            //when re-enabling a child node that was previously selected, it should disable their children
            toggleChildrenNodes(disableChildren || dtnode.childList[chIdx].isSelected(), dtnode.childList[chIdx]);
        }
    }
    
    $('#myTree').dynatree({
        selectMode: 2,
        checkbox: true,
        onSelect: function(isSelected, dtnode) {
            if(!dtnode)
                return;
    
            toggleChildrenNodes(isSelect, dtnode);
    
            //gets the optimized selected nodes
            var dryNodes = $.map(dtnode.tree.getSelectedNodes(), function(currentSelectedNode, idx) {
                if (!currentSelectedNode.data.hideCheckbox) { //skip redundant nodes
                    return currentSelectedNode.data.key;
                }
                return null;
            });
            console.log(dryNodes);
        }
    });
    

    Additionally, if the tree nodes expansion is lazy, it is necessary to handle the new children nodes to keep the consistency:

    onLazyRead: function(dtnode) {
        if(!dtnode)
            return;
    
        //[...] business logic
    
        //[...] possible iteration to add N children
    
        dtnode.addChild({
            //[...] other properties
    
            //creates childnode with the checkbox hidden if the parent is selected or hidden due to a higher ancestor
            hideCheckbox: dtnode.isSelected() || dtnode.data.hideCheckbox
        });
    }