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 $hashKeys.
<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)
endComparison 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.