Angular Tree Directive with Lazy Loading - angularjs

We are kicking a new project using angularJS :) and one of the requirements is a tree control.
The tree control should support "Lazy Loading", So we could add nodes to it dynamically as we get more data using AJAX.
I saw the 2 directives below, but I do not think "Lazy Loading" is supported
so before I start developing it myself I am asking you folks :)
The 2 nice tree directives I saw:
https://github.com/eu81273/angular.treeview/blob/master/angular.treeview.js
https://github.com/nickperkinslondon/angular-bootstrap-nav-tree
Thanks, Avi

Because AngularJS loads directives before any logic, you cannot use them very well for recursive operations, else you probably wouldn't need a directive for one in the first place.
However, one very pleasant workaround is using ng-include, as this doesn't have the above limitation. And then making a tree is extremely simple.
Where you want to tree, you have something like
<ul class="tree-container">
<li style="margin: 5px; list-style: none;" ng-repeat="d in vm.features" ng-include="'tree_item_renderer.html'"></li>
</ul>
then the include looks something like this
<script type="text/ng-template" id="tree_item_renderer.html">
<span ng-click="d.expand = !d.expand;vm.getChildNodes(d)" ng-show="!d.expand && d.hasChildren"><i class="fa fa-fw fa-plus-circle"></i></span>
<span ng-show="d.expand && d.hasChildren" ng-click="d.expand = !d.expand;d.children=null" ><i class="fa fa-fw fa-minus-circle"></i></span>
<span ng-show="!d.hasChildren" style="margin-left:28px"></span>
<ul>
<li style="list-style:none;" ng-repeat="d in d.children" ng-model="d" ng-include="'tree_item_renderer.html'"></li>
</ul>
</script>
In the controller vm.getChildNodes(d) call you can get the children for the currently expanded node. I expand the nodes and then actually asynchronously do a count for each node over odata to determine if the node has children, so I can show if one of the children has children of its own, but of course you could more efficiently track that in the database if you have control over that (I think I will update my model for this myself).
Note that I've implemented it so that if you open and close and then open, it actually reloads the nodes. You don't have to do that of course, but it saves me from having to implement a reload/refresh button otherwise and it's not like users are going to open/close trees over and over because they have nothing better to do. ;)
The added advantage I have is that I implement user input for some (most) of the nodes, e.g. select them, enter a value for them. I've noticed it is more efficient if these only exist 'on demand' in angular.

Related

Evaluate AngularJS expression within Material Design Lite's mdl-menu

I am building a tiny single-page-application using Angular for the functionality and mdl for the design. I wanted to have a dropdown select box and since mdl doesn't provide this out of the box I built one using mdl-menu. The problem is now that Angular seems to have troubles evaluating expressions I put inside this menu, maybe because the menu is only opened when you click the button? I tried using expressions within the list items and this gives the expression output but also the original expression, which I obviously don't want. I also tried to ng-repeat the list items and then they just don't show up.
<button id="demo-menu-lower-left"
class="mdl-button mdl-js-button mdl-button--icon">
<i class="material-icons">more_vert</i>
</button>
<ul class="mdl-menu mdl-menu--bottom-left mdl-js-menu mdl-js-ripple-effect"
for="demo-menu-lower-left">
<li class="mdl-menu__item">{{5+5}}</li>
<li class="mdl-menu__item mdl-menu__item--full-bleed-divider">Another Action</li>
<li disabled class="mdl-menu__item">Disabled Action</li>
<li class="mdl-menu__item">Yet Another Action</li>
</ul>
Result pic (Look at the evaluation in the top left, but the original expression is the actual "clickable" action)
Thx in advance!
MDL's javascript (denoted by the mdl-js CSS classes) will probably fool around with the HTML before Angular can do anything, eventually creating HTML that gives strange results when 'parsed' by Angular. It would be best to ensure your HTML gets processed in a strict order: Angular first, MDL second.
If you need to work with the HTML after the initial loading and rendering of the page, it would help to inspect the HTML in the browser instead of relying on the source HTML. Any javascript code (including Angular) will have to work with this and not the HTML represented by the source.

quick-ng-repeat does not update view

I use quick-ng-repeat Quick-ng-repeat for my list to iterate because it is a huge list and I would improve performance.
Now I recognized that if I change the model, the view is not updated with this code:
<span quick-ng-repeat="item in collection track by $index" quick-repeat-list="items">
{{ item }}
</span>
With this code
<span ng-repeat="item in collection track by $index">
{{ item }}
</span>
every works fine.
Does anyone have any idee why this happens?
Thanks a lot!
quick-ng-repeat does not implement deep watch just like ng-repeat, it implement one way binding approach. So if your model changes frequently, don't use it.
In Angular 1.2 a new addition was made to the syntax of ngRepeat: the amazingly awesome track by clause. It allows you to specify your own key for ngRepeat to identify objects by, instead of just generating unique IDs.
This means that you can change the above to be ng-repeat="task in tasks track by task.id" and since the ID would be the same in both your original tasks and the updated ones from the server – ngRepeat will know not to recreate the DOM elements and reuse them
The Quick ng-repeat is doing one way binding ,it something similar to what we do in angularjs
<div ng-repeat="item in ::items">{{item.name}}</div>
This does not create unnecessary watches.
I think Quick-ng-repeat use Single binding same as in angular 1.4 like (::).Single binding mean it will be create a watcher for your repeat that why it will not effect when object is change.
<div ng-repeat="obj in ::objList">{{obj.name}}</div>

SlyRepeat and ngInclude asking for transclusion on

I need to display a potential huge treeview in my webapplication. During testing I found out that using the standard template rendering method of angular with ngRepeat and ngInclude are not performing enough (it takes around 5 seconds to show the info). Removing binding (because there is no use for it) gave no results.
After some googling I found a module called scalyr https://github.com/scalyr/angular This should really optimize the performance of rendering.
My code:
<script type="text/ng-template" id="tree_item_renderer.html">
{{data.name}}
<ul>
<li sly-repeat="data in data.childNodes " ng-include="'tree_item_renderer.html'"></li>
</ul>
</script>
<ul>
<li sly-repeat="data in vm.rows" ng-include="'tree_item_renderer.html'"></li>
</ul>
When running this code I get an exception
Error: [$compile:multidir] Multiple directives [slyRepeat, ngInclude] asking for transclusion on: <li sly-repeat="data in data.childNodes " ng-include="'tree_item_renderer.html'">
Is this fixable? Or is it impossible to run scalyr with ng-Include.
There is a problem with multiple transclusions on the same element, but there's an easy solution:
<li sly-repeat="data in data.childNodes ">
<div ng-include="'tree_item_renderer.html'"></div>
</li>
There is a module that offer multiple transclusions, but I've never played with it, so I'm not sure that it works.
When you figure out your tree, tell me if the sly-repeat is worth it, as I've got a huge tree to build as well.

ngRepeat $scope messing with nested ngClick

So i have four progress bars that on click open and close via the close button in the top right....problem is the ngrepeat is messing with something....i've tried adding $parent to the child ngClick but it doesnt work. I've looked at all the other stack examples of this and just can't seem to figure out how to apply it to this specific situation
http://codepen.io/anon/pen/JorZoE
<div class="progress-bar repeat-animation" ng-click="showClose = false" ng-class="!showClose ? 'grow' : ''" progress-morph style="width: {{item.percent}}%" ng-repeat="item in list">
<div class="close" ng-hide="showClose" ng-click="onClickClose($event)" ><img src="close42.svg" alt=""></div>
</div>
I assumed that you wanted to open/close the bars individually.
If that's the case, your code wasn't working because you were binding all the progress bars state to the same $scope variable.
Having that in mind, I tweaked your code a little bit to make it work, and also used a more readable logic (imho).
Please take a look and let me know:
http://codepen.io/anon/pen/WbZygb?editors=101

correct strategy to generate html dynamically with angular

I have a huge JSON object tree with two levels. First level has around 500 elements, and each element contains an average of 100 child elements.
I want to display the first level of the tree and I am doing it with a simple ng-repeat. When the user clicks on the element I want to display the child elements of that element. If I use a span ng-switch or a ng-show to show/hide child elements when the page first renders it freezes for around 10 seconds while generating all the HTML.
It doesn't sound like the right solution. There must be a different way of doing it, but I can't figure out. Anyone knows?
I have explained most in my comment, and here is a working plunker:
http://plunker.co/edit/RSZwfLlsCJ68MUkACbdp?p=preview
the new ng-if directive will do what you want
<h1>ng-if</h1> <h5>Click on the level to expand</h5>
<div class="well">
<ul class="nav nav-list" ng-repeat="(attr,element) in tree">
<li ng-click="expand=!expand" ng-class="{'active':expand}"><a>{{element.name}}</a></li>
<ul ng-if="expand" class="nav nav-list">
<li ng-repeat="item in element.items">{{item.name}}</li>
</ul>
</ul>
</div>
you can also do this in the "old-way" with ng-show using new ternary operator or its alternative expr && if_true || if_false
<h1>old-way</h1> <h5>Click on the level to expand</h5>
<small>use ternary operator or <pre>expand && element.items || []</pre></small>
<div class="well">
<ul class="nav nav-list" ng-repeat="(attr,element) in tree">
<li ng-click="expand=!expand" ng-class="{'active':expand}"><a>{{element.name}}</a></li>
<ul ng-show="expand" class="nav nav-list">
<li ng-repeat="item in (expand ? element.items : [])">{{item.name}}</li>
<!--<li ng-repeat="item in (expand && element.items || [])">{{item.name}}</li>-->
</ul>
</ul>
</div>
See this answer on ng-repeat performance. Essentially, it just takes a long time since Angular's ng-repeat, and basically all other directives, are set up to always look for updates in the whole JSON structure. So if you have lots of data and don't need live updates in the HTML view when changing the JSON, I wouldn't recommend using AngularJS. Generally, AngularJS performance also depends a lot on the browser and its JavaScript engine.
Alternatively, you could divide your JSON into subparts and then use pagination to display it.
I would recommend to fetch the data gradually from the server. Use server-side pagination and retrieve only the fields that you are going to display. Then, when a user clicks on one of the first level items, you can do another XHR to the server with the new data. I had similar requirements for a project and that solved the latency issue.
Regards,
Agustin.

Resources