![]()
When working with custom directives in AngularJS, performance can suffer due to unnecessary re-renders caused by excessive $digest cycle executions. Preventing these re-renders is crucial for optimizing application performance.
1. Why Do Directives Re-Render Unnecessarily?
Directives in AngularJS re-render when:
✔ Scope variables change → $watch or $watchCollection detects changes.
✔ Parent scope updates → Data-binding propagates updates.
✔ Frequent DOM updates → Overuse of ng-repeat, ng-if, or deep watches.
Example of Unnecessary Re-Render
app.directive("customDirective", function () {
return {
restrict: "E",
template: "<div>{{ data }}</div>",
scope: {
data: "="
}
};
});
Here, every time data updates, the directive re-renders, even if the value hasn’t meaningfully changed.
2. Techniques to Prevent Unnecessary Re-Renders
1️⃣ Use One-Time Binding (::)
If data doesn’t change after initial render, use one-time binding:
<custom-directive data="::staticData"></custom-directive>
This prevents AngularJS from watching for future updates.
2️⃣ Avoid Deep Watching ($watch)
By default, $watch tracks deep object changes, leading to unnecessary digest cycles. Instead, use shallow watching:
$scope.$watch("userData", function (newVal, oldVal) {
if (newVal !== oldVal) {
console.log("Updated!");
}
}, false); // false = Shallow watch (default)
Avoid deep watching (true), which tracks every nested property change.
3️⃣ Use $watchCollection Instead of $watch
For arrays/objects, $watchCollection improves performance by only watching top-level changes:
$scope.$watchCollection("userList", function (newList, oldList) {
console.log("User list changed!");
});
4️⃣ Implement track by in ng-repeat
ng-repeat can cause re-renders if objects lack unique identifiers. Use track by for efficiency:
<div ng-repeat="item in items track by item.id">
{{ item.name }}
</div>
This prevents unnecessary DOM manipulations when items remain unchanged.
5️⃣ Use bindOnce for Performance (angular.bind)
To bind scope variables only once inside directives:
link: function (scope) {
scope.boundValue = angular.copy(scope.data);
}
This prevents future re-evaluations.
6️⃣ Use scope: false When No Isolated Scope Needed
If your directive doesn’t modify data, avoid creating an isolated scope:
app.directive("efficientDirective", function () {
return {
restrict: "E",
template: "<div>{{ parentData }}</div>",
scope: false // Uses parent scope, preventing unnecessary digest cycles
};
});
3. Optimized Directive Example
Here’s an optimized directive with minimal re-renders:
app.directive("optimizedDirective", function () {
return {
restrict: "E",
template: "<div>{{ ::data.name }}</div>", // One-time binding
scope: {
data: "="
},
link: function (scope) {
scope.localCopy = angular.copy(scope.data); // Prevents extra watchers
}
};
});
4. Best Practices for Avoiding Re-Renders
Use :: one-time bindings for static data.
Minimize $watch usage and prefer $watchCollection.
Use track by in ng-repeat to prevent redundant re-renders.
Disable deep watching ($watch with true) unless necessary.
Use scope: false if directive doesn’t need an isolated scope.
Leverage $cacheFactory for caching repeated calculations.
