Tell child directive to act after a parent directive has done DOM actions?

孤街浪徒 提交于 2019-12-06 15:35:12

Another way of achieving this is to use plain Angular scope events to communicate from the parent linking function to the child.

var app = angular.module('app',[]);

app.directive('bigPoppa', function($q){
  return {
    restrict: 'E',
    link: function(scope, el, attrs){
      scope.$broadcast('bigPoppa::initialised',  {el: el, title: 'Something'});
    }
  }
});

app.directive('babyBird', function(){
  return {
   restrict: 'E',
    link: function(scope, el, attrs) {
      scope.$on('bigPoppa::initialised', function(e, componentThatNeedsDom) {
        console.log('poppa DOM component in bird linking function', componentThatNeedsDom); 
      }); 
    }
  }
});

This can be seen working at http://jsfiddle.net/michalcharemza/kerptcrw/3/

This way has the benefits:

  • Has no scope watchers
  • Instead of depending on knowledge of the order of controller/pre-link/post-link phases, it uses a clear message send/receive paradigm, and so I would argue is easier to understand and maintain.
  • Doesn't depend on behaviour being in the pre-link function, which isn't that typical, and you have to be mindful to not put in behaviour that modifies the DOM in it.
  • Doesn't add variables to the scope hierarchy (but it does add events)
SimplGy

Based on experiments, and asking correction if I am wrong, I have found Angular runs its compile phase in the following order:

  1. compile methods of all directives both parent and child, run in flat order
  2. parent controller
  3. parent pre-link
  4. (all actions of children directives)
  5. parent post-link (AKA regular `link` function)

The public gist of this experiment is here: https://gist.github.com/SimpleAsCouldBe/4197b03424bd7766cc62

With this knowledge, it seems like the pre-link callback on the parent directive is a perfect fit. The solution looks like this:

    var app = angular.module('app',[]);

    app.directive('bigPoppa', function($q){
        return {
            restrict: 'E',
            compile: function(scope, el) {
                return {
                    pre:  function(scope, el) {
                      console.log('bigPoppa pre');
                      scope.componentThatNeedsDom = { el: el, title: 'Something' };
                    }
                };
            }
        }
    });

    app.directive('babyBird', function(){
        return {
            restrict: 'E',
            link: function(scope, el, attrs, bigPoppaController){
                console.log('babyBird post-link');
                console.log('bigPoppa DOM-dependent component', scope.componentThatNeedsDom);
            }
        }
    });

http://jsfiddle.net/a5G72/1/

Thanks to @runTarm and this question for pointing me in the pre-link direction.

There are 2 patterns that you can use to achieve what you want

  • You can have code in a child linking function that reacts to changes in a parent directive's controller, by requireing the parent directive's controller, and creating a $watcher on some value in it.

  • If you need run something in the parent linking function, and only then change a value in its controller, it is possible for a directive to require itself, and access the controller from the linking function.

Putting these together in your example becomes:

var app = angular.module('app',[]);

app.directive('bigPoppa', function($q){
  return {
    restrict: 'E',
    require: 'bigPoppa',
    controller: function($scope) {
      this.componentThatNeedsDom = null;
    },
    link: function(scope, el, attrs, controller){
      controller.componentThatNeedsDom = { el: el, title: 'Something' };
    }
  }
});

app.directive('babyBird', function(){
  return {
    restrict: 'E',
    require: '^bigPoppa',
    link: function(scope, el, attrs, bigPoppaController){
      scope.$watch(function() {
          return bigPoppaController.componentThatNeedsDom
      }, function(componentThatNeedsDom) {
          console.log('poppa DOM component in bird linking function', componentThatNeedsDom);
      }); 
    }
  }
});

Which can be seen at http://jsfiddle.net/4L5bj/1/ . This has the benefits over your answer that it doesn't depend on scope inheritance, and doesn't pollute the scope with values that are only used by these directives.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!