Working with ng-repeat (PART 2)
Track by ID
By default, ng-repeat creates a new DOM element for each value in the array or object that we iterate over. But to optimize performance, it caches or reuses DOM elements if the objects are exactly the same, according to the hash of the object (calculated by AngularJS).
In some cases, we might want AngularJS to reuse the same DOM element, even if the object instance does not hash to the same value. That is, if we have objects coming from a database and we do not care about the exact object properties, we want AngularJS to treat two objects with the same ID as identical for the purpose of the repeat. For this purpose, AngularJS allows us to provide a tracking expression when specifying our ng-repeat:
Here we have two arrays, notes1 and notes2, which are identical in all respects. Both of them are shown in the UI using the ng-repeat directive. The difference is that one uses the plain vanilla ng-repeat (ng-repeat=”note in ctrl.notes1″) and the other uses the track by ID version (ng-repeat=”note in ctrl.notes2 track by note.id”).
We also included an ng-click, which we used before. This allows us to trigger a function in our controller whenever someone clicks that element. In this case, we call change Notes() on our controller. The function changes the notes arrays to a new array.
Now we can see that the hashKeys and the DOM elements in the first ng-repeat are getting changed every time we click a button. In the second ng-repeat, there is no $$hashKey that AngularJS needs to generate, because we tell it what the unique identifier is for each element. So the DOM elements are reused based on the ID of the object.
Do not use any variables that start with $$ in your application. AngularJS uses them to denote private variables that it uses for its own purposes, and does not guarantee their presence or continued working across different versions of AngularJS. If you find yourself reaching out to a $$ variable, stop! You need to rethink your approach.
We would use the track-by expression to optimize DOM manipulation in our application. This would generally be on the IDs of objects returned from our databases, to ensure AngularJS reuses DOM elements even if we fetch the data multiple times from the server.
ng-repeat Across Multiple HTML Elements
An uncommon requirement, but something that still pops up every now and then, is the ability to repeat multiple sibling HTML elements that may not be in a single container element. For example, think of the case where we need to repeat two table rows (>tr<) for each item in our array, maybe one as a header row and one as a child row.
For these kinds of situations, AngularJS provides the ability to mark where our ngrepeat starts and tell which HTML element is considered part of the ng-repeat. It does so through the use of ng-repeat-start and ng-repeat-end directives:
In this example, we are creating a table to display the list of notes. For each note, we want a row where we display the label of the note, followed by a second table row where we display the status of the note. This could very well contain the author information, when it was created, and so on. Because we can’t have a wrapper element around the tr table row element, we can use the ng-repeat-start and ng-repeat-end directives.
We mark the first tr as where our ng-repeat starts, and use our traditional ng-repeat expression as the argument. We then define our template, which contains the first element with the label, and then move on to the second table row element. We mark this element as where our repeater ends by using the ng-repeat-end directive.
AngularJS will then ensure that it creates both tr elements for each element in the array we are repeating.
We covered the very basic features of AngularJS, and introduced some commonly used directives like ng-repeat and ng-click. We used these to show arrays and objects in the UI, as well as to handle user interactions and style applications conditionally. We also saw how to create controllers, and how to get data from our controllers into our views. Finally, we did a deep dive into the ng-repeat to see what it is and how we could use it in a variety of situations.
As we mentioned earlier, AngularJS has a huge focus on unit testing. To this end, each and every part of AngularJS is easy to unit test. In the upcoming blog, we will see how we might write some simple unit tests for the controllers we have written so far. We will cover how unit testing is done in AngularJS, how to set up Karma (which is used to run unit tests in AngularJS), and how we can instantiate controllers and set our expectations on their behavior.