问题
I have one auto-carousel directive which iterates through the linked element\'s children.
The children however are not yet loaded in the DOM, because their ng-ifs expressions have not been parsed yet.
How can I make sure the parent directive knows there have been changes to it\'s DOM tree?
<ul class=\"unstyled\" auto-carousel>
<li class=\"slide\" ng-if=\"name\">{{name}}</li>
...
<li class=\"slide\" ng-if=\"email\">{{email}}</li>
</ul>
I could use $timeout but that feels unreliable. I could also use ng-show instead of ng-if but that does not answer the question and not what I need.
回答1:
So here's what I ended up doing:
I discovered you could pass a function to $scope.$watch. From there, it's pretty straightforward to return the value of the expression you want to watch for changes. It will work exactly like passing a key string for a property on the scope.
link: function ($scope, $el, $attrs) {
$scope.$watch(
function () { return $el[0].childNodes.length; },
function (newValue, oldValue) {
if (newValue !== oldValue) {
// code goes here
}
}
);
}
I am watching childNodes, not children, because the childNodes list holds elements as well as text nodes and comments. This is priceless because Angular uses comment placeholders for directives like ng-repeat, ng-if, ng-switch and ng-include which perform transclusion and alter the DOM, while children only holds elements.
回答2:
If you need to watch for any changes deeper in the element's dom, MutationObserver is the way to go :
.directive('myDirective', function() {
return {
...
link: function(scope, element, attrs) {
var observer = new MutationObserver(function(mutations) {
// your code here ...
});
observer.observe(element[0], {
childList: true,
subtree: true
});
}
};
});
回答3:
I created a directive module for this angular-dom-events
In your case you could
<ul class="unstyled" auto-carousel>
<li class="slide" ng-if="name" dom-on-create="nameCreated()">{{name}}</li>
<li class="slide" ng-if="email" dom-on-destroy="emailDestroyed()">{{email}}</li>
</ul>
Currently only supports dom-on-create and dom-on-destroy, but has better performance then the accepted answer because it will only fire once for each dom event, rather than repeatedly check the $watch callback.
回答4:
Although I don't think it is with angular's recommendations, you could use ng-init which fires upon the initialization of the element:
<ul class="unstyled" auto-carousel>
<li class="slide" ng-if="name" ng-init="recheck()">{{name}}</li>
<li class="slide" ng-if="email" ng-init="recheck()">{{email}}</li>
</ul>
回答5:
You could try to compile the directive contents first inside your link function. For example:
angular.module('myApp').directive('autoCarousel', ['$compile', function ($compile) {
return {
templateUrl: 'views/auto-carousel.html',
restrict: 'A',
replace: true,
link: function (scope, element, attr) {
$compile(element.contents())(scope);
// your code goes here
}
}
}]);
来源:https://stackoverflow.com/questions/21332671/angularjs-watch-dom-change