If your custom directive is not updating the DOM, it’s likely due to scope binding issues, incorrect directive configurations, or digest cycle problems. Below, I’ll explain step by step how to identify and fix this issue.
1️⃣ Understanding the Problem
You have a custom directive that modifies the DOM, but changes do not reflect when the model updates.
Example Problem:
app.directive("myDirective", function() {
return {
restrict: "E",
scope: {
value: "=" // Two-way binding
},
template: "<p>The value is: {{value}}</p>"
};
});
<input type="text" ng-model="inputValue">
<my-directive value="inputValue"></my-directive>
Expected Behavior: The <p>
tag should update whenever inputValue
changes.
Actual Problem: The <p>
tag does not update dynamically.
2️⃣ Common Causes & Solutions
Cause 1: Scope Binding Issue
When using scope: { value: "=" }
, ensure the parent scope is correctly binding the value.
Incorrect Code (Parent scope is primitive)
<input type="text" ng-model="inputValue">
<my-directive value="inputValue"></my-directive>
Problem: If inputValue
is a primitive (string
, number
), the child directive does not detect changes because AngularJS two-way binding (=
) works best with objects.
Solution: Use an Object for Binding
<input type="text" ng-model="data.inputValue">
<my-directive value="data.inputValue"></my-directive>
$scope.data = { inputValue: "Hello" };
Now, updates in the parent reflect inside the directive.
Cause 2: Directive Uses Isolated Scope Without Proper Watchers
If your directive has an isolated scope, Angular does not automatically track changes unless explicitly told to do so.
Incorrect Code (No $watch
)
app.directive("myDirective", function() {
return {
restrict: "E",
scope: { value: "=" },
template: "<p>The value is: {{value}}</p>",
link: function(scope) {
// No watcher means it won't update dynamically
}
};
});
Problem: Without $watch
, AngularJS does not detect updates properly.
Solution: Add $watch
to Detect Changes
app.directive("myDirective", function() {
return {
restrict: "E",
scope: { value: "=" },
template: "<p>The value is: {{value}}</p>",
link: function(scope) {
scope.$watch("value", function(newVal) {
console.log("Value updated:", newVal);
});
}
};
});
Now, the directive updates whenever value
changes.
Cause 3: Not Triggering AngularJS Digest Cycle
Sometimes, changes happen outside Angular’s digest cycle, preventing the UI from updating.
Incorrect Code (External Event Not Wrapped in $apply
)
app.directive("myDirective", function() {
return {
restrict: "E",
scope: { value: "=" },
template: "<p>The value is: {{value}}</p>",
link: function(scope, element) {
element.on("click", function() {
scope.value = "Updated!";
});
}
};
});
Problem: DOM does not update because Angular does not detect changes made outside its scope.
Solution: Use $apply()
or $timeout()
app.directive("myDirective", function($timeout) {
return {
restrict: "E",
scope: { value: "=" },
template: "<p>The value is: {{value}}</p>",
link: function(scope, element) {
element.on("click", function() {
scope.$apply(function() {
scope.value = "Updated!";
});
});
}
};
});
Now, clicking the element updates the UI.
Cause 4: Using ng-model
in a Directive Without ngModelController
If your directive involves ng-model
, you must use require: "ngModel"
to correctly bind data.
Incorrect Code (Does Not Use ngModelController
)
app.directive("customInput", function() {
return {
restrict: "E",
scope: {},
template: '<input type="text">',
link: function(scope, element) {
element.on("input", function() {
scope.modelValue = element.val(); // Does not sync with ng-model
});
}
};
});
Problem: This does not update the model properly.
Solution: Use require: "ngModel"
app.directive("customInput", function() {
return {
restrict: "E",
require: "ngModel",
template: '<input type="text">',
link: function(scope, element, attrs, ngModelCtrl) {
element.on("input", function() {
scope.$apply(function() {
ngModelCtrl.$setViewValue(element.val());
});
});
ngModelCtrl.$render = function() {
element.val(ngModelCtrl.$viewValue);
};
}
};
});
Now, the directive properly updates ng-model
.
Cause 5: Using transclude
Without ng-transclude
If your directive uses transclusion (transclude: true
), but the template lacks ng-transclude
, the DOM will not update correctly.
Incorrect Code (Missing ng-transclude
)
app.directive("myDirective", function() {
return {
restrict: "E",
transclude: true,
template: "<div><p>Content:</p></div>"
};
});
Problem: The transcluded content does not appear.
Solution: Use ng-transclude
in the Template
app.directive("myDirective", function() {
return {
restrict: "E",
transclude: true,
template: "<div><p>Content:</p><div ng-transclude></div></div>"
};
});
Now, transcluded content appears inside the directive.