I'm trying to access data from a service so I can manipulate an ngRepeat
ed list with jQuery. I cannot get the link
function to work. Based on what I've read, I would expect that in either
app.directive('myDir', function() {
return {
restrict: 'E',
templateUrl: 'url.html',
link: function(scope, el, attrs) {
//jQuery to manipulate DOM here
}
};
});
or
app.directive('myDir', function() {
return {
restrict: 'E',
templateUrl: 'url.html',
compile: function(scope, el, attrs) {
return {
post: function() {
//jQuery to manipulate DOM here
}
}
}
}
});
But the elements are not on the page yet, so it does not work. What am I missing?
Try waiting one $digest phase. This will give enough time to the ng-repeat
to render the DOM elements. You can use $timeout for that.
link: function(scope, el, attrs) {
$timeout(function() {
//jQuery to manipulate DOM here
});
}
Check the example below. Here we have my-dir
collecting all the elements created by the ng-repeat
. There is a helper mockup-box
inner directive that include my-dir
parent directive controller and registers itself ( its element ) using its methods.
Then my-dir
controller calls the invalidateChildren function which waits for the children directive to accumulate by delaying the validateChildren
function execution.
In the validateChildren
function we manipulate the DOM elements and we are sure that they'll be there because each one registers only when it's compiled. There is some dummy code in validateChildren function but you can place anything there.
There is another added benefit. We no longer depend on the ng-repeat nor does my-dir knows how the child elements will be created. They might even be "static" children. It also supports adding additional children. If the ng-repeat generates additional elements they'll again register in the parent controller and invoke the validateChildren function.
angular.module('example', []) .controller('AppCtrl', function($scope) { $scope.mockupData = ['a', 'b', 'c', 'd'] }) .directive('myDir', function() { return { controller: function($element, $timeout) { var ctrl = this; var childrenDirty = false; this.children = []; this.addChild = function(jqChild) { ctrl.children.push(jqChild); ctrl.invalidateChildren(); }; this.removeChild = function(jqChild) { var index = ctrl.children.indexOf(jqChild); if (index >= 0) { ctrl.children.splice(index, 1); } }; this.invalidateChildren = function() { if (!childrenDirty) { childrenDirty = true; $timeout(ctrl.validateChildren); } }; this.validateChildren = function() { childrenDirty = false; // Will be fire only once angular.forEach(ctrl.children, function(jqChild) { jqChild.html('value: "' + jqChild.html() + '"'); }); }; }, link: function() { } } }) .directive('mockupBox', function() { return { require: '^myDir', link: function(scope, iElement, iAttrs, myDirCtrl) { myDirCtrl.addChild(iElement); scope.$on('$destroy', function() { myDirCtrl.removeChild(iElement); }); } }; });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script> <div ng-app="example" ng-controller="AppCtrl"> <div my-dir> <div ng-repeat="data in mockupData" mockup-box>{{data}}</div> </div> </div>
mockupData
value is ['a', 'b', 'c', 'd']; validateChildren
function edits the child elements content and adds "value:" in front. The result from the code execution will be value: "<letter>"
for every child element.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.