Where angular holds DOM state, and based on what criteria
Categories:
Understanding DOM State Management in AngularJS ng-repeat
Explore how AngularJS's ng-repeat
directive manages the Document Object Model (DOM) state, the criteria it uses for tracking elements, and how to optimize its behavior for performance and predictability.
AngularJS, while an older framework, introduced powerful concepts for dynamic UI rendering. One of its most frequently used directives, ng-repeat
, is central to rendering lists of data. A common question arises: how does ng-repeat
efficiently manage the DOM when data changes? Understanding where AngularJS holds DOM state and the criteria it uses is crucial for writing performant and bug-free applications.
The Role of ng-repeat
in DOM Management
ng-repeat
is designed to instantiate a template once per item in a collection. When the collection changes (items are added, removed, or reordered), ng-repeat
needs to update the DOM efficiently. Instead of re-rendering the entire list, it attempts to identify which DOM elements correspond to which data items and only perform necessary updates. This is where DOM state management comes into play.
flowchart TD A[Data Collection Change] --> B{ng-repeat detects change} B --> C{Identify existing DOM elements} C --> D{Identify new/removed data items} D --> E{Match data items to DOM elements} E --> F{Update/Add/Remove DOM elements} F --> G[DOM Updated]
Simplified ng-repeat
DOM update process
Tracking Elements with $hashKey
and track by
By default, AngularJS assigns a unique $hashKey
property to each object in a collection when it's iterated by ng-repeat
. This $hashKey
is an internal identifier used by AngularJS to track the identity of each object. When the collection changes, AngularJS compares the $hashKey
of the new items with the $hashKey
of the existing DOM elements to determine if an item is new, removed, or simply reordered. This default behavior works well for simple objects, but can lead to issues if your data items are recreated (e.g., fetched from an API) even if their logical content is the same, as they will receive new $hashKey
s.
<div ng-app="myApp" ng-controller="myCtrl">
<ul>
<li ng-repeat="item in items">{{ item.name }}</li>
</ul>
</div>
Default ng-repeat
behavior using $hashKey
To provide more control over how ng-repeat
tracks elements, AngularJS offers the track by
expression. This allows you to specify a property on your data items that should be used as the unique identifier instead of the internally generated $hashKey
. This is particularly useful when dealing with data from a backend where items have stable, unique IDs (like a database primary key).
<div ng-app="myApp" ng-controller="myCtrl">
<ul>
<li ng-repeat="item in items track by item.id">{{ item.name }}</li>
</ul>
</div>
Using track by
with a unique id
property
track by
with a stable, unique identifier when working with ng-repeat
and dynamic data. This significantly improves performance by preventing unnecessary DOM re-rendering and preserves the state of child directives and elements within the repeated block.Impact on DOM Element State and Performance
When ng-repeat
re-renders an element without track by
(or with a track by
that results in new identities for logically same items), the old DOM element is destroyed, and a new one is created. This means any internal state of that DOM element (like scroll position, focus, input values, or state managed by other directives) is lost. Using track by
correctly ensures that if an item's identity remains the same, its corresponding DOM element is reused and updated, preserving its internal state.
flowchart LR subgraph Without 'track by' A[Data Item 1] --> B(DOM Element 1) C[Data Item 1 (recreated)] --> D(New DOM Element 2) B -- Destroy --> E(Lost State) end subgraph With 'track by' F[Data Item 1 (ID:1)] --> G(DOM Element 1) H[Data Item 1 (ID:1, updated)] --> G G -- Update --> I(Preserved State) end
Comparison of DOM state preservation with and without track by
The performance implications are also significant. Destroying and recreating DOM elements is an expensive operation. By reusing existing elements, track by
minimizes DOM manipulation, leading to smoother animations, faster rendering, and a more responsive user interface, especially for large lists.
track by $index
, AngularJS will track items by their position in the array. While this can be useful in specific scenarios (e.g., when items are truly interchangeable and order is the only differentiator), it can lead to incorrect DOM state preservation if items are reordered or inserted/deleted in the middle of the array, as the $index
of subsequent items will change.