Search code examples
javascriptangularjsangular-directive

Angular directive - need to access required controller from directive controller


I'm writing an angular "tab" directive which needs to communicate with a parent "tabs" directive by sending a reference to itself to the parent.

I am trying to keep my code in the directive's controller, not the link function, because I'm not really doing anything with DOM.

The problem is that if i use {require: '^tabs'} i can get a copy of the required directive's controller like this..

link: function(scope, element, attr, ctrls) {
    var controller = ctrls[0];
}

but how would I do this inside the directive's controller function?


Solution

  • This is always a fun problem for me. I like to think that I am playing on the wild side with my solution to this common case.

    First, I take a step back to the controllerAs view syntax as suggested in the the angular style guide (https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#controlleras-controller-syntax)

    Using this syntax you can define your directive as:

        var directive = {
            require: '^myParentDirective',
            restrict: 'EA',
            controller: 'MyController as myCtrl',
            templateUrl: 'super/awesome/app/my/my.directive.html',
            scope: {
                myProp: '='
            }, // isolate scope
            link: link
        };
    
        function link(scope, element, attr, myParentCtrl) {
    
            scope.myCtrl.myParent = myParentCtrl;
    
        }
    

    The catch (there's always a catch):

    Remember that a directive's controller is constructed before the link function is invoked. So you cannot expect the myParent property to be resolved in the code executed during the controller's construction.

    function MyController($scope) {
    
        var _self = this;
    
        _self.myParent.register(_self); // myParent is not defined
    
    }
    

    Instead wait until you are sure myParent is resolved

    function MyController(scope) {
    
        var _self = this;
    
        _self.initialize = initialize;
    
        return;
    
        function initialize() {
    
            _self.myParent.register(_self); // myParent might be defined
    
        }
    
    }
    

    then do something like

    function link(scope, element, attr, myParentCtrl) {
    
        scope.myCtrl.myParent = myParentCtrl;
    
        scope.myCtrl.initialize();
    
    }
    

    Let me know if this helps, is off base, or if you need more clarification.

    (Also, please excuse syntax errors)