Probably one of the very first things you learned doing with Angular was how to use ngRepeat
. It’s so easy and simple, you gotta love it. Here’s an example:
1 2 3 4 5 |
|
The problem
Now say you have a button above that list to refresh it.
The obvious implementation for this refresh would be something along these lines in your controller:
1
|
|
This is a trivial piece of code, but it would cause ngRepeat
to remove all li
elements of existing tasks and create them again, which might be expensive (e.g. we have a lot of them or each li
’s template is complex). That means a lot of DOM operations. That would make us sad pandas.
In something of a more real-world use case, where instead of our simple example template each task is its own directive, this might be very costly. A task directive that calculates a view model with even relatively simple operations like formatting dates is enough to make the refresh feel laggy. There’s a link to a fiddle with an example at the bottom of the post.
Why would Angular do this? Behind the scenes ngRepeat
adds a $$hashKey
property to each task to keep track of it. If you replace the original tasks with new tasks objects from the server, even if those are in fact totally identical to your original tasks, they won’t have the $$hashKey
property and so ngRepeat
won’t know they represent the same elements.
The annoying solution
The first solution that comes to mind to most developers is to not replace the whole $scope.tasks
list, but update in place all the existing tasks objects with the data you received from the server. That would work because it means $$hashKey
property would be left intact in the original objects.
But that’s not fun, is it? I hate surgically tinkering with objects, as it is error prone, less readable and a pain to maintain.
track by
to the rescue
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. Boom.
To get a better feel of the difference this makes, see this fiddle. By default it doesn’t use track by
and the date formatting it does is enough to make clicking “Refresh” feel noticeably slow on my computer. Try adding the track by
and see how it magically becomes instant, because the task
directive’s link function is no longer called on each refresh.
It’s really that easy to get a quick little boost in performance that also saves you writing annoying code.
“Maintaining AngularJS feels like Cobol 🤷…”
You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete.
Components? Lifecycle hooks? Controllers are dead?
It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines!
Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.
Subscribe and get my free email course with steps for upgrading your AngularJS app to the latest 1.6 safely and without a rewrite.