简体   繁体   中英

Angular directive's link function won't manipulate DOM

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
    });
}

EDIT:

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> 

Expected behavior

  • 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM