angular-ui-tree icon indicating copy to clipboard operation
angular-ui-tree copied to clipboard

Super slow, hangs the browser

Open jehanzebqayyum opened this issue 10 years ago • 31 comments

I tried 1 node with 5000 children. First demo on http://jmlimu.github.io/angular-ui-tree. It hangs the browser every time and i had to kill it. Even thougg angular repeat data binding works easily with this load.

Please assist is such a load supported?

jehanzebqayyum avatar Apr 06 '15 09:04 jehanzebqayyum

that's quite a lot. Try using something like angular-vs-repeat to ease the load on the DOM

chino23 avatar Apr 07 '15 04:04 chino23

I'd recommend the bindonce directive to reduce the amount of watchers for properties that don't change once they are set.

william-poitras avatar Apr 07 '15 14:04 william-poitras

:+1: bindonce @william-poitras

brianfeister avatar Apr 10 '15 20:04 brianfeister

Thanks will try. For the time being switched to jstree for the treepart only On 7 Apr 2015 6:06 pm, "william-poitras" [email protected] wrote:

I'd recommend the bindonce directive to reduce the amount of watchers for properties that don't change once they are set.

— Reply to this email directly or view it on GitHub https://github.com/angular-ui-tree/angular-ui-tree/issues/424#issuecomment-90571398 .

jehanzebqayyum avatar Apr 12 '15 19:04 jehanzebqayyum

No effect even with angular 1.3 bindonce ::, I face the same issue. Only with around 800 nodes it works normally. More than that it is unable to handle. On 12 Apr 2015 11:07 pm, "jz" [email protected] wrote:

Thanks will try. For the time being switched to jstree for the treepart only On 7 Apr 2015 6:06 pm, "william-poitras" [email protected] wrote:

I'd recommend the bindonce directive to reduce the amount of watchers for properties that don't change once they are set.

— Reply to this email directly or view it on GitHub https://github.com/angular-ui-tree/angular-ui-tree/issues/424#issuecomment-90571398 .

jehanzebqayyum avatar Apr 26 '15 07:04 jehanzebqayyum

Well the problem here is probably not the watchers but the heavy DOM load, it depends how heavy each node is. Like I mentioned earlier, https://github.com/kamilkp/angular-vs-repeat will probably solve your problem.

Check out http://kamilkp.github.io/angular-vs-repeat/#?tab=1 , put it to 5000 or 100000 and click "show content"

chino23 avatar Apr 27 '15 08:04 chino23

We're also running into performance problems with the tree, particularly on mobile. We have 190 nodes and on some relatively modern mobile devices it takes a good 5-6 seconds to render the tree. What's more, there are some 1750 angular watchers created by all this, even after flagging all of our template's bindings as bind-once.

Our tree structure is highly nested, which unfortunately means that something like angular-vs-repeat is not usable because the length of what's visible varies.

Is there a way to get angular-ui-tree to only process branches when they are expanded? That would probably considerably speed up our data set, because at start all of the top-level branches are collapsed except for 1.

doublemarked avatar Jun 17 '15 23:06 doublemarked

Is there a way to get angular-ui-tree to only process branches when they are expanded? That would probably considerably speed up our data set, because at start all of the top-level branches are collapsed except for 1.

Solved this by replacing a stylesheet toggle with an ng-if. Significant improvement.

doublemarked avatar Jun 18 '15 00:06 doublemarked

I actually had to abandon this module as it took like really 8 seconds to load 120 nodes. Even with the ng-if fix. Pure jquery runs it in about 1 sec. To bad, i really like this module, but the speed is unacceptable for the user :(

chino23 avatar Jun 18 '15 04:06 chino23

Same problem : about 300 nodes along in about 5 levels in the tree, browser hangs some seconds when rendering the tree, even with all nodes collapsed...

I changed for a ng-if="!collapsed" in the child node list, but no perf improvement...

<script type="text/ng-template" id="nodes_renderer.html">
        <div ui-tree-handle class="tree-node tree-node-content">
            <span ng-if="node.nodes && node.nodes.length > 0" data-nodrag ng-click="toggle(this)" class="text-info">
                <i class="glyphicon" ng-class="{'glyphicon-chevron-right': collapsed,'glyphicon-chevron-down': !collapsed}"></i>
            </span>

            <a ng-href="{{node.url}}">
                <img ng-show="node.icon" ng-src="{{node.icon}}" /> {{node.title}}
            </a>
        </div>
        <ol ui-tree-nodes ng-model="node.nodes" ng-if="!collapsed">
            <li ng-repeat="node in node.nodes" ui-tree-node ng-include="'nodes_renderer.html'" collapsed="node.collapsed">
            </li>
        </ol>
    </script>

maitredede avatar Sep 10 '15 01:09 maitredede

this is very critical issue as I see it - having huge problems because of it as well with > 650nodes. I created exactly same structure of data using plain css + angular's ng-repeats + ng-if and everything became insanely faster.

f1ght4fun avatar Sep 14 '15 12:09 f1ght4fun

Please share your alternative solution. When i tried with nested angular templates, i couldn't nest nodes more than 10 levels, hit angular limit. On 14 Sep 2015 4:25 pm, "f1ght4fun" [email protected] wrote:

this is very critical issue as I see it - having huge problems because of it as well with > 650nodes. I created exactly same structure of data using plain css + angular's ng-repeats + ng-if and everything became insanely faster.

— Reply to this email directly or view it on GitHub https://github.com/angular-ui-tree/angular-ui-tree/issues/424#issuecomment-140054283 .

jehanzebqayyum avatar Sep 14 '15 15:09 jehanzebqayyum

Just curious, did any of you manage to find solutions to using this plugin with large datasets?

I'm almost thinking Angular itself isn't meant to handle this. I mean, the golden rule is that you shouldn't have more than ~2000 watchers on your page, but if you have even one watcher per ui-tree-node with 2000 tree-nodes... you're done for.

Ideally you could go for something like virtual scrolling, but I can see that being very difficult to implement with such a complex UI that is collapsable, draggable, etc.

Does any one have thoughts on this? I've found this plugin works great for small datasets, but for larger ones, maybe Angular just isn't the right tool for the job.

BlakeBrown avatar Nov 11 '15 21:11 BlakeBrown

I love this library and use it in some parts.

However with large datasets I was forced to switch to fancytree. It's jQuery though, but it works.

I'm sure once Angular 2 goes out of alpha and this module gets ported it's the first choice again!

chino23 avatar Nov 12 '15 03:11 chino23

I had similar issue but have used below approach solved performance issue.



<script type="text/ng-template" id="renderer.html">
    <div ui-tree-handle>
        <a class="btn btn-custom btn-xs" data-nodrag ng-click="toggle(this)">
                                                <span ng-if="item.children.length > 0">
                                                    <i class="fa fa-2" ng-class="{'fa-chevron-right': collapsed, 'fa-chevron-down': !collapsed}"></i>
                                                </span>
                                            </a> {{item.elementCode}} {{item.elementName}}
    </div>
    <ol ui-tree-nodes="options" ng-model="item.children" ng-if="!collapsed" data-nodrop>
        <li ng-repeat="item in item.children" collapsed="true" ng-if="!collapsed" ui-tree-node ng-include="'renderer.html'"></li>
    </ol>
</script>
<div ui-tree="options">
    <ol ui-tree-nodes ng-model=mainContent data-nodrop>
        <li ng-repeat="item in mainContent" ui-tree-node collapsed="true" ng-if="!collapsed" ng-include="'renderer.html'" class="animate-repeat"></li>
    </ol>
</div>

gipsyprsd avatar Jan 18 '16 10:01 gipsyprsd

@gipsyprsd What is the difference? Could you please explain?

burakkilic avatar Jan 19 '16 18:01 burakkilic

@gipsyprsd I'm not seeing anything special there, can you elaborate on what you've done and what performance issue it solved please?

AndrewIsh avatar Jan 22 '16 11:01 AndrewIsh

Using ng-if to remove children if the node is collapsed does a lot of difference in performance. In spite of this, when I start expanding nodes, the whole application starts to get slow again...

pelizza avatar Feb 16 '16 13:02 pelizza

I've been experiencing these performance issues lately and I've tried the solutions in this thread. ng-if instead of ng-class makes a big difference but only when not many things are expanded, as @pelizza said. So while it's an improvement it's a shame to have to teach users to keep closing things behind them for performance. I'm interested to see any new tips people have found

SubJunk avatar Apr 12 '16 23:04 SubJunk

The notes from gipsyprsd have really helped a lot...ng-if makes a huge difference. Earlier it used to take me 120 seconds...and with this correction it gets down to 5 to 6 seconds

srikandula avatar Sep 05 '16 18:09 srikandula

I will look into this as soon as possible, poor performance could kill this project and we don't want that!

jacobscarter avatar Sep 06 '16 04:09 jacobscarter

This is a huge problem for us and I can't find a viable workaround. What have others done to successfully replace this?

SuzannePoirierVS avatar Sep 08 '16 14:09 SuzannePoirierVS

@SuzannePoirierVS I am working on fixing this PR, hopefully we will have this performance PR merged in a couple of days. I will post an update here shortly.

jacobscarter avatar Sep 08 '16 16:09 jacobscarter

Hey everyone, I thought I'd leave a few of my thoughts here since I worked on performance issues with this library at my last job.

tldr; If you plan on having more than 100 elements in your tree, Angular is not the tool for the job.

As many of you probably know, Angular uses dirty checking in order to determine which DOM elements need updating. Every node in your angular-ui-tree requires watchers in order to determine when they need to be updated. This can amount anywhere from 5-10 watchers per tree node, since Angular will use watchers to keep track of a node's position as well as any bindings (ng-if, ng-click, etc). More watchers = a slower application, and if you have 100 tree nodes * 10 watchers per node your application is going to be very slow.

Use the following to check how many watchers you have on your page. If that number is above 2000, then you've just found the reason why your page is lagging.

So by now you're probably asking yourself "wow Blake... how can I fix this?" Fortunately, it's pretty straight forward.

You need to re-think the design of your application. Do you need 50+ nodes on the screen at once? What if you only load the ones that are actually visible on the screen and load the rest later/when the user scrolls? Is there anyway you could change how the user interacts with the tree nodes? You should encourage collapsing of the nodes, reducing the number of total nodes on a single page, etc.

If you go through all of the above and decide that there's no possible way to reduce the number of tree nodes on the screen then you have another answer. You can't use Angular, you'll need to go with either a faster single-page app framework like react.js or a library that doesn't implement dirty checking like jQuery.

The owners of this repository have done a great job creating an intuitive, modifiable UI tree. Unfortunately, there are limitations of the frontend framework itself.

That's just my $0.02, hope it helps :)

BlakeBrown avatar Sep 08 '16 18:09 BlakeBrown

Some very helpful advice @BlakeBrown. Today is one of those rare days that I'm glad I didn't mute a Github thread I chimed in on years ago.

I'm using lazy-loading for my childNodes to keep watcher count down, though my UI does allow end users to screw things up with an unlimited number of first-level children. I suppose some day I'll be implementing infinite scroll there when someone stupidly creates 1,000 folders at the same level.

Is that what you're doing already? I'd like to point out that infinite scroll to address this problem is something that, in an ideal world, this directive would handle.

brianfeister avatar Sep 14 '16 18:09 brianfeister

Any movement here this has just become critical for the project I'm working on.

RamonDonnell avatar Oct 10 '16 03:10 RamonDonnell

Is there still any progress? The ng-if="!collapsed" fixed the inital loading problem for me, but I'm currently working on a search function which should expand the selected (from the search results) item in the tree. The problem is, that the children are not loaded because of ng-ifand trying to get them with elem.childNodes() returns null. I get the inital tree with var tree = angular.element(document.getElementById('<tree-id>')).scope();. But after expanding the first level (using tree.$nodesScope.childNodes()[0].expand()) and adding the children to the expanded element's modelValue (tree.$nodesScope.childNodes()[0].modelValue.children = getChildren(<id>);), childNodes() on that element returns null. If I run my search function several times, it always expands one more level. E.g. for an element on level 5 with should be selected, I have to call my method 6 times. 5 times to expand all parents and a last one to set the element as current element. Is there a way to get the current state of the tree or update my variable tree?

Thanks in advance and thanks for the project so far!

v1r0x avatar Dec 06 '16 16:12 v1r0x

@v1r0x Same problem here, to solve the lag produced by big trees, had to use ng-if instead of ng-show. That boosted performance but broke the "filter" functionality since the collapsed node children were no longer in the DOM.

The solution we found was to check the tree information in the model, rather than the DOM. Here's what we did, in a nutshell:

  1. Add a collapsed property to the model of your nodes and use the data-collapsed attrib so ui-tree-node knows about it. For convenience I also added a visible property so ng-if can decide if the node should be rendered.
    <ol data-nodrag ui-tree-nodes="" ng-model="node.nodes" ng-if="!collapsed && node.visible">
      <li ng-if="node.visible" ng-repeat="node in node.nodes" data-collapsed="node.collapsed" ui-tree-node ng-include="'nodes_renderer.html'" collapsed="true">
      </li>
    </ol>
  1. Write a function that recursively traverses the tree model, and evaluates the filter for each node. When a node matches, I set node.visible=true on it and node.collapsed=false on it's parent.
        vm.processFilter = function(){
        	if(!$scope.filter || $scope.filter.length == 0 || $scope.filter.length > 3){
        		vm.myTreeModel.forEach(function(node){vm.visible(node);}
        	}
        }

        vm.visible = function (node) {
        	if(vm.filters(node)){
    			node.collapsed = false;
        		node.visible = true;
        	} else {
        		node.visible = false;
        	}
		// recursively evaluate the children
        	for(var i in node.nodes){
        		if(vm.visible(node.nodes[i])){
                                // if any of the children is visible, this node should also be visible and expanded
        			node.collapsed = false;
        			node.visible = true;
        		};
        	}
        	return node.visible;
        };     

        vm.filters = function(node) {
        	if(!$scope.filter || $scope.filter.length == 0){
        		node.highlighted = false;
        		return true;
        	} else {
               		if(node.name.toUpperCase().indexOf($scope.filter.toUpperCase()) != -1){
                		node.highlighted = true;
               			return true;
               		}
        		node.highlighted = false;
	            	return false;
         	}
        }

  1. Finally, add a ng-change to the filter input, where you evaluate the visibility of the tree

<input id="filterBox" ng-model="filter" ng-change="vm.processFilter();">

You can see I added a condition, not to evaluate filters below three characters length. This is to avoid the entire tree being expanded at once when the user starts to type the filter with a very common character (like 'a').

On a side note, we used node.highlighted property to attach a css class to the nodes that match the filter.

Hope it helps.

achousa avatar Jan 12 '17 16:01 achousa

@mrsernine thanks for your answer! Didn't expect that someone would answer the question. In the meantime I found a solution myself. I think your solution is better, I'll definitely have a look at it.

Here is a rough overview of what I did:

  1. Get the id of the element which should be highlighted and selected and the ids of all it's parent (ordered by their "distance" to the desired element)
  2. Get the tree by its DOM class (var t = angular.element(document.getElementById('tree')).scope();) and its nodeScope (var nodesScope = t.$nodesScope;)
  3. Now I recursivly get the children of the current nodesScope and check if the id of one of the children matches the id of the desired element (from step 1) or the first element in the parents array (also from step 1). If it matches the first parent I remove the parent id from the parents array and repeat this step with its children and the new parents array.

It is a bit hacky, but works very well and the app doesn't lack anymore. The expanding seems to work very fast too.

Thanks again for your solution!

v1r0x avatar Jan 12 '17 16:01 v1r0x

+1

FranclisJunior avatar Dec 12 '17 14:12 FranclisJunior