I have a div element with ng-bind directive:
<div ng-bind="sometext"></div>
I have a directive that gets an element, checks its value / text and adds a color to the element according to the content. I am using this directive like that:
<div ng-bind="sometext" my-directive></div>
The problem is that on the time the directive is executing, there is no value or text on the div because ng-bind didn't happened yet.
I am getting the text using element.text().
Any idea how to make the text available inside my directive?
I have a div element with ng-bind directive:
<div ng-bind="sometext"></div>
I have a directive that gets an element, checks its value / text and adds a color to the element according to the content. I am using this directive like that:
<div ng-bind="sometext" my-directive></div>
The problem is that on the time the directive is executing, there is no value or text on the div because ng-bind didn't happened yet.
I am getting the text using element.text().
Any idea how to make the text available inside my directive?
3 Answers
Reset to default 13Your directive may be running before ngBind
has bound it's value - both your directive and ngBind
are priority 0 so either could run first, more on that in a moment- but let's look at the ngBind
source code to see the root of the problem:
var ngBindDirective = ngDirective(function(scope, element, attr) {
element.addClass('ng-binding').data('$binding', attr.ngBind);
scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
element.text(value == undefined ? '' : value);
});
});
We see that ngBind
doesn't immediately update the DOM, but rather places a watch on the ngBind
attribute. So the element won't be updated until that watch is run on the next $digest
cycle (which is why a $timeout
works).
So one option is to mimic ngBind
and place your own watch on it's attribute- then you'll be updated whenever the ngBind
result changes:
angular.module('myApp').directive('myDirective', function() {
return {
priority: 1,
link: function(scope,element,attrs) {
scope.$watch(attrs.ngBind, function(newvalue) {
console.log("element ",element.text());
});
}
};
});
You'll note I set priority to 1. You do need to be sure that this directive's watch is placed after the ngBind
watch in the watch queue. That will ensure the element has been updated by ngBind
first.
By default a directive's link function runs post-link, so as the $compile
docs note:
Directives with greater numerical priority are compiled first. Pre-link functions are also run in priority order, but post-link functions are run in reverse order.
Therefore, since ngBind
is priority 0, anything over 0 will ensure your directive's watch will come after the ngBind
watch..
demo fiddle
Edit 2
The other option is to use ng-class
or ng-style
for changing the color of the text. Then you don't have to create a new directive at all.
Original Answer
I would not depend on the ng-bind
directive at all... This seems much cleaner in my opinion.
<div ng-bind="someModel" my-directive="someModel"></div>
And then define your directive as...
angular.module('myApp').directive('myDirective', function() {
return {
link: function(scope, element, attrs) {
scope.$watch(attrs.myDirective, function(newValue, oldValue) {
// Your Code here...
});
}
};
});
This way you can use your directive even if you don't have an ng-bind
on the element (for example, if you use curly braces instead).
<div my-directive="someModel">{{someModel}}</div>
Alternatively you can use attrs.$observe(...)
(documentation) instead of scope.$watch(...)
.
<div ng-bind="someModel" my-directive="{{someModel}}"></div>
and
angular.module('myApp').directive('myDirective', function() {
return {
link: function(scope, element, attrs) {
attrs.$observe('myDirective', function(interpolatedValue) {
// Your Code here...
});
}
};
});
You can find more information about the differences between scope.$watch(...)
and attrs.$observe()
here.
Edit
Given that your directive is basically mutating the DOM after the ng-bind
directive, why not skip the ng-bind
all together?
<div my-directive="{{someModel}}"></div>
and
angular.module('myApp').directive('myDirective', function() {
return {
link: function(scope, element, attrs) {
attrs.$observe('myDirective', function(interpolatedValue) {
if (interpolatedValue) {
// Modify interpolatedValue if necessary...
}
element.text(interpolatedValue == undefined ? '' : interpolatedValue);
});
}
};
});
You could use a scope.$watch in you link function and watch for model changes. Each time the value changes ng-bind will fire and your directive will be fired as well, independent to the bg-bind directive and dependent only to the modle itself.
Sorry i cant presnt you with sample code as im on a tablet while answering your question.
$timeout
around part of my code, so it's evaluated the next digest cycle when the binding has happened. I'm not entirely happy with that solution, but perhaps it suffices for your needs. – towr Commented Jan 11, 2014 at 20:36priority: 1
to your directive? just as a curiosity, maybe it really is that simple? – Matt Greer Commented Jan 11, 2014 at 20:43