I'm new to Angular, but I've already heard (and read) some "rumors" about its rendering model and how it is different from for example React.
I've read some posts of Angular experts who claim that if you have to render long lists with Angular, it could be slow because Angular would re-render the whole list if something changes, while React (for example) "won't re-render the whole list from scratch once it has already been rendered but it will keep track of rendered DOM elements internally and upon new invocation create new virtual DOM, pare it to the previous one and only apply the changes"
(quoted from a random blog post about Angular rendering issues)
So as I started learning Angular, my first thing was trying this out.
And it seems that I cannot reproduce the problem...
Here is a dummy Plunker which I created to reproduce the issue.
You can add new items to a list of messages which is rendered with ng-repeat like this:
<table>
<tr ng-repeat="m in messages" class="{{name}}">
<td>{{m.message}}</td>
<td>{{m.date}}</td>
</tr>
</table>
You can click on a button which could update one such item, and you can update another property which is totally unrelated to the list (name
)
Now if I open developer toolbar, and modify an item's HTML attributes inside the table and then I click on "add a new message", my modifications don't get lost or overwritten by Angular - it seems that Angular doesn't re-render the DOM fully. It seems to be pretty smart.
Has Angular started to use DOM diffing lately? (My demo uses Angular 1.4.0 beta)
Just because from the DOM rendering point of view, I just don't see the big difference between React and Angular.
Could you show me a use case which show the drawback of Angular's rendering model?
I'm new to Angular, but I've already heard (and read) some "rumors" about its rendering model and how it is different from for example React.
I've read some posts of Angular experts who claim that if you have to render long lists with Angular, it could be slow because Angular would re-render the whole list if something changes, while React (for example) "won't re-render the whole list from scratch once it has already been rendered but it will keep track of rendered DOM elements internally and upon new invocation create new virtual DOM, pare it to the previous one and only apply the changes"
(quoted from a random blog post about Angular rendering issues)
So as I started learning Angular, my first thing was trying this out.
And it seems that I cannot reproduce the problem...
Here is a dummy Plunker which I created to reproduce the issue.
You can add new items to a list of messages which is rendered with ng-repeat like this:
<table>
<tr ng-repeat="m in messages" class="{{name}}">
<td>{{m.message}}</td>
<td>{{m.date}}</td>
</tr>
</table>
You can click on a button which could update one such item, and you can update another property which is totally unrelated to the list (name
)
Now if I open developer toolbar, and modify an item's HTML attributes inside the table and then I click on "add a new message", my modifications don't get lost or overwritten by Angular - it seems that Angular doesn't re-render the DOM fully. It seems to be pretty smart.
Has Angular started to use DOM diffing lately? (My demo uses Angular 1.4.0 beta)
Just because from the DOM rendering point of view, I just don't see the big difference between React and Angular.
Could you show me a use case which show the drawback of Angular's rendering model?
Share Improve this question edited Mar 16, 2015 at 0:45 user2864740 62.1k15 gold badges158 silver badges228 bronze badges asked Mar 16, 2015 at 0:38 ZsoltZsolt 3,2833 gold badges35 silver badges49 bronze badges 4- 5 This doesn't seem 'too broad' to me. The primary question the OP is asking is about a specific mechanic of Angular.. – user2864740 Commented Mar 16, 2015 at 0:45
-
3
Your questions is a bit broad because it really asks several questions, and may be a better fit on Programmers SE. Anyway, Angular doesn't diff like React, but ng-repeat does keep up with what it is repeating and the associated elements. It will only change what needs to be changed. See the
track by
option ofng-repeat
. Angular's tracking efficiency in this matter is why duplicates are not allowed inng-repeat
unless you usetrack by
option to change what Angular watches to keep things in sync. – m59 Commented Mar 16, 2015 at 0:46 - 5 Investigating the veracity of blog posts that make unsubstantiated claims about certain technologies isn't really our specialty, nor is it the specialty of Programmers.SE. – Robert Harvey Commented Mar 16, 2015 at 0:53
- 2 This is a perfectly answerable question. The answer would give an overview of the steps angular takes in the digest loop to bring about the change, and why the manual modifications aren't lost. Side note: the blog post's benchmarks are no where near accurate, but that's not the main question here. – Brigand Commented Mar 16, 2015 at 1:32
1 Answer
Reset to default 7There is quite a bit of confusion regarding this topic, mostly because it's not a simplistic "angular re-renders everything" type answer.
The basis of angular data binding (in 1.x releases) surrounds around the concept of a $digest
loop and $scope
. $scope
is a special object which "self tracks" it's properties, and creates a JavaScript event listener for each property, using the method $scope.$watch()
. These listeners monitor changes to the changeable input elements in HTML, and to the $scope
properties.
Whenever any JavaScript listener fires, the $digest
loop cycles through every item under $watch
and updates the values appropriately. You also can call $scope.$apply()
to manually execute a $digest
loop. This loop can execute multiple times, as changes to one value can affect another value under $watch
which triggers another $digest
. The $digest
loop does have an iteration cap to ensure it stops on circular references, however.
The rub es in when you are dealing with arrays of objects, and the special directive ng-repeat
. By default, the $watch()
function only checks object reference equality. Within each $digest
, AngularJS will check to see if the new and old values are the same "physical" object, and will only invoke its handler if you actually change the underlying object reference.
To counter this, ng-repeat
creates it's own unique scope
. This allows for a unique $watch
for every element in the array. The challenge here is that if the array itself changes, then this unique scope
is regenerated, along with all the $watch
elements. Pushing, Popping, Splicing an array can create many $watch
values.
Angular provides a few ways to deal with this issue.
The new Bind Once syntax added in 1.3 allows for Listeners which only exist until the expression is evaluated. ng-repeat="element in ::elements"
Will iterate through the array, populate the DOM, then destroy the Event Listener. This is ideal for situations where the DOM element does not change once evaluated.
It is also possible to aggressively track elements, using track by
. ng-repeat="element in elements track by $id"
will create a $watch
on the unique $id
value, rather than the element's position in the array. This allows for more stable $watch
propagation, and is ideal in cases where the value of the element may change.
As to why the changes you made in the console were not lost: Firstly, changes in the developer console are not going to trigger any Event Listeners. Secondly, the specific DOM element you changed would only be modified if the $watch
detected a change. This is not really a 'Diff' of the HTML, however; Angular isn't "watching the HTML", it is "watching the data", so to speak.