Bindonce on ngRepeat - javascript

What is the effect of using the native bindonce on an ng-repeat object? For example:
ng-repeat="data in ::stuff"
Does this mean every item in 'stuff' has the watcher removed? Or do you still need to apply bindonce to every child bind in the repeat like this?
<div ng-repeat="data in ::stuff">
<span ng-bind="::data.thing"></span>
</div>

For data in ::stuff, the array is bound once and a $watcher is not created after bound the first time, and therefore any changes to that array will not update your ng-repeat's view.
However, unless you have ::data.thing changes to individual objects will still be registered. Those watchers belong to the object itself and not the shallow contents of the array.
See my plunkr below.
<iframe src="http://embed.plnkr.co/3gbmI2kqd3rT7z0GEyK7/"></iframe>

Related

Getting DOM element in AngularJS directive

I´m trying to get a DOM element in order to change it inside a AngularJS1.0.6 directive.
HTML:
<li ng-repeat="car in cars" data-highlight="{{car.id}}">
Directive:
var iw = angular.element(document.querySelector('#iw-' + id));
console.log("iw=" + iw);
Please see the plunker for details: https://plnkr.co/edit/dP2cvut4f5ao5pFe6ka0?p=preview
After little research, I was able to make it work. Basically first issue comes when you use directive inside ng-repeat (also directive).
In this case directive's scope must be set for example like: scope: {car: '=highlight'}. This can desribe what happens. I would rather use <ul highlight="cars"></ul> along with directive's template like '<ul><li ng-repeat="car in cars"></li></ul>'. So ng-repeat would be inside directive.
When you get through, another issue comes in form of syncing two directives (different scopes). You need DOM ready to select element from another directive. I saved your compliled element to car object - it can be count as workaround that make it easier.
Forked plunker here
<li ng-repeat="car in cars track by $index" data-highlight="{{car.$index}}">
or
if whatever property exists, in your case, car must have id property

AngularJS: ngTransclude and access scope of ngRepeat

We are in the process of upgrading our application to Angular 1.3.0. In doing so, we ran into a few issues, most of which seem to boil down to the behavior of ngTransclude inside of ngRepeat.
We have a directive that repeats a bunch of items, with a container around them, but does not own the children of that container. For instance, here is a simplified example:
<div ng-controller="myController">
There are {{items.length}} items.
<div my-directive items="items">
This item's name is {{item.name}}
</div>
</div>
Internally, the directive contains <li ng-repeat="item in items" ng-transclude></li>, among other things.
Prior to the update, this worked fine. The repeated, transcluded elements are in a scope that inherits from the scope created by ngRepeat. As of the update, the items are in a scope that inherits from the controller, and as far as I can tell, there is no way to access the scope created by ngRepeat.
Here are two JS Bin examples:
Old (1.3.0-beta.1) behavior: http://jsbin.com/kalutu/1/edit
New (1.3.0) behavior: http://jsbin.com/gufunu/1/edit
How can I achieve the old behavior, or some semblance of it, in Angular 1.3.0? If this is the intended behavior of ngTransclude, how can I repeat a bunch of child nodes without knowing what they are?
https://github.com/angular/angular.js/issues/8182
It was decided for 1.3 that ng-trasclude would not pull scope from the directive. There is a work-around on the linked pages,
https://github.com/angular/angular.js/issues/7874
https://github.com/angular/angular.js/issues/7874#issuecomment-47647528
This is the expected behavior.

Understanding the ngRepeat 'track by' expression

I'm having difficulties understanding how the track by expression of ng-repeat in angularjs works. The documentation is very scarce: http://docs.angularjs.org/api/ng/directive/ngRepeat
Can you explain what the difference between those two snippets of code is in terms of databinding and other relevant aspects?
with: track by $index
<!--names is an array-->
<div ng-repeat="(key, value) in names track by $index">
<input ng-model="value[key]">
</div>
without (same output)
<!--names is an array-->
<div ng-repeat="(key, value) in names">
<input ng-model="value[key]">
</div>
You can track by $index if your data source has duplicate identifiers
e.g.: $scope.dataSource: [{id:1,name:'one'}, {id:1,name:'one too'}, {id:2,name:'two'}]
You can't iterate this collection while using 'id' as identifier (duplicate id:1).
WON'T WORK:
<element ng-repeat="item.id as item.name for item in dataSource">
// something with item ...
</element>
but you can, if using track by $index:
<element ng-repeat="item in dataSource track by $index">
// something with item ...
</element>
a short summary:
track by is used in order to link your data with the DOM generation (and mainly re-generation) made by ng-repeat.
when you add track by you basically tell angular to generate a single DOM element per data object in the given collection
this could be useful when paging and filtering, or any case where objects are added or removed from ng-repeat list.
usually, without track by angular will link the DOM objects with the collection by injecting an expando property - $$hashKey - into your JavaScript objects, and will regenerate it (and re-associate a DOM object) with every change.
full explanation:
http://www.bennadel.com/blog/2556-using-track-by-with-ngrepeat-in-angularjs-1-2.htm
a more practical guide:
http://www.codelord.net/2014/04/15/improving-ng-repeat-performance-with-track-by/
(track by is available in angular > 1.2 )
If you are working with objects track by the identifier(e.g. $index) instead of the whole object and you reload your data later, ngRepeat will not rebuild the DOM elements for items it has already rendered, even if the JavaScript objects in the collection have been substituted for new ones.

AngularJS Outer ng-repeat not to register watches on inner ng-repeat

This is in relation with my question - Angular JS ng-repeat consumes more browser memory
My problem here is I need nested ng-repeats and the nested ng-repeats consumes more memory because of more watches being registered.
<table>
<thead><td>Id</td><td>Name</td><td>Ratings</td></thead>
<tbody>
<tr ng-repeat="user in users | orderBy:'name' | limitTo:display_limit">
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td><div ng-repeat="item in items | orderBy:'rating' | limitTo:inner_display_limit">{{item.rating}}</div></td>
</tr>
</tbody>
</table>
Here in my case, the number of objects that outer ng-repeat and inner ng-repeat operates on can go upto 1000. And as #Liviu pointed in the answer,each of the outer ng-repeat registers watch on the inner ng-repeat and that leads to consumable amount of memory being used. Is there a way we can avoid the outer ng-repeat from registering watches on inner ones by writing our own custom directive?
My case is in both inner and outer ng-repeats, I display the initial 50 items and on scroll, if the scroll reaches the end of the corresponding DOM, I update the limit by 50, so that the next 50 items gets displayed.
Any help is much appreciated!
You could write a directive that takes both users and items as bindings attributes (especially because it seems like your inner loop is independant from the outer one), then manually nest two loops that would add content to the DOM, plus adding a scroll listener to each element. It's more hard-way coding but a lot less watchers.

editing variable while iterating over it in angulajrs

I'm getting an error saying that I reached 10 $digest iterations. Other questions like this one, tell me that that is because I'm changing a variable while iterating over it. The problem is that I'm making an interface in which that would be really useful to do. My code currently looks like this.
<div ng-repeat="(index, trigger) in triggers" ng-click="select(index)">
<strong ng-bind="trigger.name></strong>
<div ng-show="selected == index" class="span12">
<h5>edit properties</h5>
<div ng-repeat="property in getTriggerProps(trigger.type) ">
<div ng-bind="property.name"></div>
<div ng-switch on="property.name">
<input type="text" ng-switch-when="'text'" ng-model="trigger[property.name]">
<select type="select" ng-switch-when="'select'" ng-model="trigger[property.name]" ng-options="possible for possible in property.options">
</select>
</div>
</div> </div>
</div>
In my real code, there are a lot more twitter bootstrap related divs and classes, but I've left them out for clarity.
So, what this does is iterate over a collection named triggers. There are different types of triggers, and the properties of these triggers are returned by a function on the scope called `getTriggerProps.
For the list of returned properties, I create a series of inputs based on the property type. The model of the property, will be the actual property on the trigger object. But that's the problem I think I'm having, I'm changing properties of the trigger while iterating over the collection this trigger is in.
I'm using angularjs 1.1.5, which I'm aware is an unstable version, but I wanted to play with its features.
In case seeing the entire page I'm working on could help, here's a public link:
https://dl.dropboxusercontent.com/u/12041395/neurobot_interface_proposal.html
So, changing the objects your iterating on isn't a problem. The way ng-repeat works is, it tags the objects with a hashKey that's like a unique ID, so it knows they're the same instance. Just adding properties wouldn't break that.
I think your issue may be that you're iterating over newly created objects? Not sure what getTriggerProps does, but if it creates new objects every invocation, that will break for sure. If that's the case, the solution is to add your own hashKey property to each one, which will be the same every time if the object is conceptually the same.
The name property would probably be a good choice to use as a hash key.

Categories

Resources