We use cookies to give you the best experience on our website. If you continue to browse, then you agree to our privacy policy and cookie policy. Image for the cookie policy date
close icon

How to disable or hide CHILD nodes in tree control?

Guys, I've been trying to figure out how to disallow selecting certain children nodes inside of a Treeview control, to no avail.  Here is the set up:

(1) I have an ASP.NET MVC Treeview control set up as follows:

@Html.EJS().TreeView("BMItemTree").ShowCheckBox(true).NodeChecked("nodeCheckedBM").Fields(field =>
                    field.Id("itemId").ParentID("parentId").Text("itemName").HasChildren("hasChild").Expanded("expanded").HtmlAttributes("htmlAttr").DataSource(ViewBag.dataSourceBM)).CheckedNodes(ViewBag.checkedNodesBM).Render()

(2) HtmlAttributes are set with two classes - disableCheckbox and activeCheckbox, and display like this in source code at runtime:


(3) I have CSS styles that apply to the disableCheckbox class elements, so the class is being recognized.  However, I cannot figure out how to hide or remove the element that corresponds to a checkbox inside of the disableCheckbox class.  I've tried jQuery methods hide() and remove() as follows - it looks like var removeCheckbox is not set to anything, aka - somehow, the elements marked as ".disableCheckbox" are not being found and stored in that variable:


Here is the outcome - the CSS is applied, but the checkboxes are not deactivated and still allow to be checked (those with FR1-1, FR1-2, and FR2-1 labels). 

What am I missing here?  Many thanks!



15 Replies

SA Shameer Ali Baig Sulaiman Ali Baig Syncfusion Team December 30, 2019 12:04 PM UTC

Hi Ekaterina, 
  
Thanks for contacting Syncfusion Support. 
 
We have checked with your requirement to disable/hide the child nodes in the tree view control and would like to let you know that we can disable the tree child item by using the disableNodes method. You can either pass the array of id values of the child nodes to be disabled or the child nodes element array to disable the items in the tree view control as shown in the below code snippet. 
 
<script> 
    window.onload = function (args) { 
        var treeObj = document.getElementById("BMItemTree").ej2_instances[0]; 
        treeObj.disableNodes(document.getElementsByClassName("disableCheckbox")); 
    } 
</script> 
 
To know more about the disableNodes methods, please check the below documentation link. 
 
  
Regards, 
Shameer Ali Baig S. 



PG Pranav Gaikwad December 30, 2019 03:52 PM UTC

Hi Shameer, 

Thank you for your reply.  I tried this solution, but it did not work - it still allows me to select the child nodes that are marked with this class.  No JS errors are raised in the browser console, I know there are no syntax issues, and all the class assignments show up in the console.

Are you sure you were able to apply this solution to the CHILD nodes?  I know that it can be done to the parent nodes, but I have to disable only specific child nodes.

Thank you,
Ekaterina


SA Shameer Ali Baig Sulaiman Ali Baig Syncfusion Team December 31, 2019 10:00 AM UTC

Hi Ekaterina, 
 
We would like to let you know that the disableNodes method disables all the nodes passed as parameters irrespective to parent or child node. Since you have specified that the child nodes gets checked even if disabled, we can’t understand what is your exact requirement since the disabled nodes can’t be accessed or checked.  
 
Also we suspect your requirement might be restricting the disabled child nodes checkboxes checking while selecting the parent node of the child nodes. Else, if this is not your requirement, then kindly get back to us what is your exact requirement with disabling the child nodes in the tree view control such that we can validate and provide a solution to achieve your requirement at your end. 

Regards, 
Shameer Ali Baig S.


PG Pranav Gaikwad January 2, 2020 02:12 PM UTC

Thank you, Shameer. The requirement is:

1. Not to allow selecting a child node's checkbox which is marked with a CSS class disabledCheckbox, and 
2. Not to allow selecting a child node's checkbox which is marked with a CSS class disabledCheckbox if its parent node's checkbox is checked.

Looking at the last screenshot in my original post, this requirement would translate into the following:

 (A) When node FR (parent) is checked, none of its children nodes that have greyed out checkboxes will be selected.  For instance, if FR1-1 and FR1-2 had their corresponding checkboxes greyed out but FR2-1 did not, clicking on the parent node would not select them, but would select FR2-1 (if FR2-1 was not greyed out).

(B) When any children nodes with greyed out checkboxes under a non-greyed out parent are clicked on, their checkboxes are not checked.  In other words, in the screenshot as it is now, none of the nodes - FR1-1, FR1-2, or FR2-1 can be checked.

I hope this helps to understand the exact requirement.

Thank you,
Ekaterina


PG Pranav Gaikwad January 3, 2020 02:08 AM UTC


PS: Please see attached sample.  Essentially, that's the functionality that I have been trying to implement but with Syncfusion EJ2.

Attachment: EJ1MVC960272838_checkboxes_tree_control_e1b78699.zip


SA Shameer Ali Baig Sulaiman Ali Baig Syncfusion Team January 7, 2020 01:05 PM UTC

Hi Ekaterina, 
 
Thanks for your patience.  
 
We have validated your requirement in EJ2 TreeView component. Unfortunately, we haven’t provided support for achieving your requirement in EJ2 TreeView. So, we have considered your query as feature request in EJ2 TreeView.  
 
The support for this feature will be included in our next Volume release which is planned to be rolled out at the end of June 2020.  
 
You can track the status of this feature through the below feedback portal link. 
 
 
 
We appreciate your patience until, then. 
 
Regards, 
Shameer Ali Baig S. 



SP Sowmiya Padmanaban Syncfusion Team July 8, 2020 11:04 AM UTC

Hi Ekaterina, 
  
We deeply regret for the inconvenience. 
  
Due to some technical difficulties, we were unable to include this feature in this Volume 2 2020 release. We are working on this issue with high priority. 
  
We will include the feature in 2020 Volume 3 release which is expected to be rolled out at the end of September 2020. 
  
We appreciate your patience. 
  
Regards, 
Sowmiya P. 



LH Lon Hofman July 8, 2020 01:16 PM UTC

Can't this be achieved by adding a handler to the nodeChecking event of Treeview? It does not visually disable the checkboxes, but it allows you to prevent a checkbox from being checked by canceling the event.
I use this myself to prevent the user to select more than a maximum number/less than a minimum number of nodes by canceling the event when the constraint is violated,  though you have to iterate child nodes if a container node is checked: ( checked : "columnVisible", hasChildren : "field" )

Where I use a counter as constraint you could modify it to use a different one (for example, the presence of a css class on the node)

function nodeChecking(args) {
    let count = this.treeData.filter(n => !n.hasChildren && this.checkedNodes.includes(n.id.toString())).length;
    const nodeId = args.data[0].id;
    const colInfo = this.fields.dataSource.find(c => c.id.toString() === nodeId);
    switch (args.action) {
    case "check":
    {
        const toCheck = [];
        if (!colInfo.field) {
            const containers = [nodeId];
            do { // use a post-check loop so we can check if there were more to check than allowed or it was just enough
                const current = containers[containers.length - 1];
                for (const item of this.fields.dataSource) {
                    //for (const item of this.getTreeData()) {
                    if (item.parentId != current) continue; // need coercion: current is string. parentId can be Number, null or undefined (so can't call .toString() on it)
                    if (item.field) {
                        if (item.columnVisible) continue;
                        count++;
                        if (count > maxVisible)
                            break;
                        if (!this.getNode(current).expanded)
                            this.expandAll([current], 1); // nodes need to be rendered for the nodeChecked event to fire
                        toCheck.push(item.id.toString());
                    } else
                        containers.push(item.id);
                }
                containers.pop();
            } while (containers.length && count <= maxVisible) // if count > max we know not all desired nodes will checked
            this.checkAll(toCheck);
        } else {
            if (this.checkedNodes.includes(nodeId))
                return; // for some reason the event gets fired twice for one of the nodes that are checked ???
            if (++count <= maxVisible) {
                toCheck.push(nodeId);
            }
        }
        if (count >= maxVisible) {
            // if maximum has been reached, disable all unchecked nodes, except the ones included in toCheck
            this.disableNodes(this.getTreeData().filter(c => c.columnVisible === false && !toCheck.includes(c.id))
                .map(n => n.id));
            if (count > maxVisible) {
                // attempt was made to select more nodes than allowed => cancel event (in case of a container select, we check the max amount of child nodes allowed ourselves)
                args.cancel = true;
            }
        }
        break;
    }
    case "uncheck":
        // we need to determine how many checkboxes will remain checked after the operation manually to ensure at least the minimum amount is preserved.
        if (!colInfo.field) {
            const toUncheck = [];
            const containers = [nodeId];
            const items = this.fields.dataSource;
            do {
                const current = containers[containers.length - 1];
                // by iterating backwards, an attempt to clear the selected fields will preserve the topmost minimum items in the list.
                for (let i = items.length - 1; i >= 0; i--) {
                    const item = items[i];
                    if (item.parentId != current) continue; // need coercion: current is string. parentId can be Number, null or undefined (so can't call .toString() on it)
                    if (item.field) {
                        if (!item.columnVisible) continue;
                        count--;
                        if (count < minVisible)
                            break;
                        toUncheck.push(item.id.toString());
                    } else
                        containers.push(item.id);
                }
                containers.pop();
            } while (containers.length && count >= minVisible)
            if (count < minVisible)
                this.uncheckAll(toUncheck); // this container was the only one with any checked boxes in it. Uncheck all except the topmost minimum
        } else {
            if (this.checkedNodes.includes(nodeId)) // exclude duplicate events
                count--;
        }
        if (count < minVisible) {
            args.cancel = true; // cancel if an attempt was made to uncheck more than allowed
        }
    }
}

I hope this helps as a workaround, and if not, I guess it didn't harm sharing an idea.
Remco Beurskens



SP Sowmiya Padmanaban Syncfusion Team July 10, 2020 03:03 AM UTC

Hi Lon,  
 
We have validated your shared code snippet with TreeView component in a sample. The expected requirement met with your workaround in our sample also. If your proposed solution meets your requirement with TreeView, then you can use your proposed solution. 
 
However, we will implement this as a feature from our end to ensure the functionality in all the possible cases in TreeView component. 
 
Refer the below link to track the feature status. 
 
 
Regards,  
Sowmiya.P  



LH Lon Hofman July 14, 2020 05:33 PM UTC

Hi,
I found there's a bug in my earlier code if there's containers within containers and the order of walking the nodes matters. ( like my sample meant to be selecting to top n nodes - in that case it should walk the child nodes in a container node first before continuing to its siblings - in that case, I guess it's easier to implement/maintain using recursion if that's the case as using a stack the way I did way won't work with non-tail recursive logic) :
Replacement code for my earlier snipplet:
 // for 'check':
// .......
        const toCheck = [];
        if (!colInfo.field) {
            const treeView = this;
            function walkNodes(containerId) {
                for (const item of treeView.fields.dataSource) {
                    if (item.parentId != containerId) continue; // need coercion: current is string. parentId can be Number, null or undefined (so can't call .toString() on it)
                    if (item.field) {
                        if (item.columnVisible || item.disabled) continue;
                        count++;
                        if (count > maxVisible)
                            break;
                        if (!treeView.getNode(containerId).expanded)
                            treeView.expandAll([containerId], 1); // nodes need to be rendered for the nodeChecked event to fire
                        toCheck.push(item.id.toString());
                    } else {
                        walkNodes(item.id); // recursive call: now the container's children are evaluated before continuing
                        if (count > maxVisible) return;
                    }
                }
            }
            walkNodes(colInfo.id);
            this.checkAll(toCheck);
        } else {
//  ......

And for 'uncheck':

/// ...
       if (!colInfo.field) {
            const toUncheck = [];
            const items = this.fields.dataSource;
            function walkNodes(containerId) {
                // by iterating backwards, an attempt to clear the selected fields will preserve the topmost minimum items in the list.
                for (let i = items.length - 1; i >= 0; i--) {
                    const item = items[i];
                    if (item.parentId != containerId) continue; // need coercion: current is string. parentId can be Number, null or undefined (so can't call .toString() on it)
                    if (item.field) {
                        if (!item.columnVisible) continue;
                        count--;
                        if (count < minVisible)
                            break;
                        toUncheck.push(item.id.toString());
                    } else {
                        walkNodes(item.id); // recursive call: now the container's children are evaluated before continuing
                        if (count < minVisible) return;
                    }
                }
            }
            walkNodes(colInfo.id); 
            if (count < minVisible)
                this.uncheckAll(toUncheck); // this container was the only one with any checked boxes in it. Uncheck all except the topmost minimum
        } else {
// ....

Kind regards,
Remco Beurskens






SP Sowmiya Padmanaban Syncfusion Team July 15, 2020 01:46 PM UTC

Hi Lon,  
 
We were unable to replicate your shared worked around solution of your application. We have tried a simple TreeView sample based on your use case with TreeView check action. Refer the below sample. 
 
 
Please, ensure the above sample help you solve your reported problem. We need to ensure the all the functionalities of TreeView component. So, we will consider this as a feature and include in our Volume 3 Release. 
 
Refer the below link to track the feature status.  
  
 
We appreciate your patience. 
 
Regards,   
Sowmiya.P  



LH Lon Hofman July 16, 2020 05:47 PM UTC

Correction: values passed to walkNodes(parentId) must be a string, so .toString() needs to be added in those calls.
NOTE: My dataSource is a local Array, not a DataManager, so, .filter would be replaced by a call to ej.data.query() in that case.

I am not sure what it is you can't get to work, so I adjusted your sample in an attempt to show my intentions:


(assuming the code below is not enough to prevent the checkbox from being checked by checking a parent node:
function drawNode(args) {
    if (args.nodeData.disabled) {
        const ele = args.node;
        ele.classList.add("e-disabled");
        ele.querySelector(".e-checkbox-wrapper").classList.add("e-checkbox-disabled");
        ele.querySelector(".e-list-text").textContent = args.nodeData.text;
    }
)

Kind regards,
Remco Beurskens


SP Sowmiya Padmanaban Syncfusion Team July 17, 2020 04:43 PM UTC

Hi Lon,  
  
We will modified your sample based on your requirement and will update the sample on three business days on 22nd July 2020. 
 
Please let us know, if you need any further assistance. 
 
Regards,  
Sowmiya.P 



LH Lon Hofman July 20, 2020 09:50 AM UTC

Of course.

I don't need an assistance at the moment. I just spotted this thread and thought I could help as I already implemented something that could meet the desired requirements.
As you said before as reply on my initial post, if it helps, just modify it to your wishes as my sample is just one scenario it would work for.
 My correction is just to make it work as well in case there are deeper nested structures in the treeview and to make sure all nodes are evaluated in the order that appear in the data source. (this could make it possible to put a restriction on the number of node to be selected as well as preventing disabled nodes to be selected)

I just hope at least the OP is helped by this.

Kind Regards,
Remco Beurskens


SP Sowmiya Padmanaban Syncfusion Team July 21, 2020 12:17 PM UTC

Hi Lon Hofman,  
 
Thanks for sharing your solution to us. 
 
In our previous update, we have just prepared simple sample to achieve your case. However, we will implement this feature in our component level and let you know the details once the feature is implemented. 
 
Refer the below link for feature status. 
 
Please let us know, if you have any concerns. 
 
Regards,  
Sowmiya.P 


Loader.
Live Chat Icon For mobile
Up arrow icon