snflurry snflurry - 4 months ago 21
AngularJS Question

Binding behavior of ng-repeat track by

Lets assume I am binding one big nested object to the $scope of the view shown in the code. Now, the value of an "e" object is updated. This would cause angular the check all bindings and update the DOM. If I used "track by" instead, in each ng-repeat directive, would that mean that only the binding for the "e" object would react and the dom for the "e" object be updated?

<div ng-repeat="a in b">
<div ng-repeat="c in a">
<div ng-repeat="d in c">
<div ng-repeat="e in d">
{{e.value}}<br>
</div>
</div>
</div>
</div>

Answer

The bindings will be checked no matter what, and updated only if different, per the digest cycle. As for re-building the DOM elements, Angular uses unique identifiers to determine whether each item in an ng-repeat already has a matching DOM element, or if it needs to render a new one.

By default, Angular creates and manages these unique identifiers under the hood, using the $id of each object (or $$hashKey).

track by was added later, as a way to tell Angular to use a unique identifier of your choice, rather than managing it under the hood.

This is useful when updating the data removes/changes the $id or $$hashKey, triggering unnecessary re-builds of each DOM element, even when the data didn't change at all.

Consider this example:

  • You have an ngRepeat which displays data records:

    <li ng-repeat="item in data">{{item.value}}</li>

  • You use a service DataService to update your data, which has a fetch() method which retrieves data from an SQL database, and returns the records.

  • Updating the data in your $scope involves calling that service, and re-assigning your data variable to the result:

    $scope.data = DataService.fetch();

That means, even if only one item was different, all the $id or $$hashKey properties are gone or different, and Angular will assume all items are new. It will re-build all the DOM elements from scratch.

However, since your data is from an SQL database, you already have a unique identifier (primary key), the id column. You could then change your ngRepeat to be:

<li ng-repeat="item in data track by item.id">{{item.value}}</li>

Now, instead of looking for $$hashKey, which gets lost every time you re-assign the data, Angular will use the property you told it to (item.id). Since that property does persist across re-assigning the variable, the list is once again optimized, because Angular will only re-build DOM elements for new items.