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.
Related
I have created an uib-accordion in my angular project, where all the uib-accordion-group elements have been created under ng-repeat(for efficiency ofcourse).
<uib-accordion close-others="oneAtATime">
<ul ng-model="some_code">
<li ng-repeat="some_code">
<div uib-accordion-group heading="{{some_code}}">
{{some_code}}
</div>
</li>
</ul>
</uib-accordion>
The problem is I want to insert different and dynamic content in the uib-accordion-group(s) but being in ng-repeat I can't use template-url. I have almost 6 rows to be displayed. Should I fall back to developing each accordion row individually(which would increase code size, hence I don't want) or do we have any such provision in uib-accordion (which maybe I am currently not aware of)?
Suggestions please.
try to use ng-class and change the styles dynamically
The html code you want to put in each accordion. Are you getting it dynamically or you have the code you just want to put different code on different condition.
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.
So,
I am rendering a basic list using ngRepeat, but need the first <li> to be static content, rather than being generated from the data array:
e.g.
<ul ng-repeat="item in items">
<li>This is text from the template and will always be the same.</li>
<li>{{item}}</li>
</ul>
I've also looked at using ng-repeat-start, but just can't quite get to a solution.
It is a common misconception that people want to use ng-repeat on the parent element, when in fact you use it on the actual elements that do the repeating.
You just need to change it to:
<ul>
<li>This is text from the template and will always be the same.</li>
<li ng-repeat="item in items">{{item}}</li>
</ul>
I have got a number of components/windows (divs) on my html page; each div meant to show a large amount of data. Am trying to use nginfinite-scroll (http://binarymuse.github.io/ngInfiniteScroll/documentation.html) on each of these components so that infinite scrolling happens inside each of them. But looks like the infinite-scrolling is triggered only when an element on which the nginfinite-scroll is called nears the end of the browser-window. But i can't have my components move. How do i get infinite scroll happen in-place, i mean inside each component/element whose position would not near the browser end? Hope, the problem statement is understood.
Here's my html:
<div id="myDiv">
<ul class="list" infinite-scroll='nextPage()' infinite-scroll-disabled="false">
<li data-ng-repeat="item in items">
<span>{{item}}</span>
</li>
</ul>
</div>
Regards
I have a HTML file it iterates over a list of objects as shown and every object has a template( stored in the db) that it uses I get "List" from a web service :-
<ul>
<li ng-repeat="object in List" ng-include="object.TemplateName" > </li>
</ul>
Let object.TemplateName be "template1"
A sample template would have a specific directive with the attributes needed and few html tags as shown "template1":-
template1:-
<directive1 s-web-service-path="object.WebServicePath" >
<h1>any html content</h1>
</directive1>
my directive calls a web service to get the content to be displayed and has its own template... instead of putting directives in a template and including them cant I directly call my directive depending upon the different types of objects that i obtain in List
something like
for Object.Type="1" i call directive1 instead of template1
for Object.Type="2" i call directive2 instead of template2
ngIf or ngSwitch might be helpful here, with a few extra wrapping elements within the ngRepeat, in order to dynamically choose what to include based on Object.Type. Using ngSwitch:
<ul>
<li ng-repeat="object in List">
<div ng-switch="object.Type">
<div ng-switch-when="'1'">
<div ng-include="object.TemplateName"></div>
</div>
<div ng-switch-when="'2'">
<directive1 s-web-service-path="object.WebServicePath" >
<h1>any html content</h1>
</directive1>
</div>
</div>
</li>
</ul>
The above is not tested, so there could potentially be an error. You might also be able to cut down on some of DOM nesting level by including the ng-switch-when attributes on the directive1 / ng-include divs, but the way above makes the behaviour clear, and avoids any unexpected issues that might arise from having multiple directives work on the same element.