I’m trying to understand interactions between the Angular world and the non-Angular world.
Given a directive that one declares like this:
<dir1 id="d1" attr1="100"/>
If code outside angular changes the directive this way:
$("#d1").attr("attr1", 1000);
How can the directive know that one of its attribute has changed?
It would be best to make this change inside the directive instead. If, for whatever reason, that's not possible, then there are a couple of options.
Outside the app, get a reference to any DOM element within the app. Using that reference, you can then get a reference to its scope. You could use your element with id d1
. For example:
var domElement = document.getElementById('d1');
var scope = angular.element(domElement).scope();
Here are a couple of options:
Modify the model instead of making a direct change to the view. In the link function, store the initial attribute value in a scope variable like:
scope.myvalue = attrs.attr1;
Then you can change the value outside the app (using the above reference to scope) like:
scope.$apply(function(){
scope.myvalue = 1000;
console.log('attribute changed');
});
Here is a fiddle
If the view is manipulated directly with jQuery, I don't know of any use of $observe
, $watch
, or an isolate scope binding to the attribute that will work, because they all bind to the attribute expression itself, just once, when the link function is first run. Changing the value will cause those bindings to fail. So you'd have to $watch
the attribute on the DOM element itself (rather than through attrs
):
scope.$watch(function(){
return $(el).attr('attr1'); // Set a watch on the actual DOM value
}, function(newVal){
scope.message = newVal;
});
Then you can change the value outside the app (using the above reference to scope) like:
scope.$apply(function(){
$("#d1").attr("attr1",1000);
});
Here is a fiddle
Use a Web Components library like x-tags by Mozilla or Polymer by Google. This option works without maunally calling $scope.$apply
every time the attribute changes.
I use x-tags because of their wider browser support. While defining a new custom tag (directive) you can set the option lifecycle.attributeChanged
to a callback function, which will fire every time an argument is changed.
The official docs aren't very helpful. But by trial and error and diving into the code I managed to find out how it works.
The callback function's context (the this
object) is the element itself. The one whose attribute has changed. The callback can take three arguments:
name
– the name of the attribute,oldValue
andnewValue
– these speak for themselves.So now, down to business:
This will watch the attribute for changes:
xtag.register('dir1', {
lifecycle: {
attributeChanged: function (attribute, changedFrom, changedTo) {
// Find the element's scope
var scope = angular.element(this).scope();
// Update the scope if our attribute has changed
scope.$apply(function () {
if (attribute == 'attr1') scope.style = changedTo;
});
}
}
});
The attributeChanged
callback only fires when the arguments' values actually change. To get their initial values you need to scan the lot manually. The easiest way seems to be while defining the directive:
myApp.directive('dir1', function () {
return {
... ,
link: function (scope, element, attributes) {
scope.attr1 = element[0].getAttribute('attr1');
}
};
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With