thatmarvin
thatmarvin

Reputation: 2880

How to access a require'd controller from another controller?

I have an Angular 1.3 module that looks something like this (directive that requires the presence of a parent directive, using controllerAs):

angular.module('fooModule', [])

.controller('FooController', function ($scope) {
  this.doSomething = function () {
    // Accessing parentDirectiveCtrl via $scope
    $scope.parentDirectiveCtrl();
  };
})

.directive('fooDirective', function () {
  return {
    // Passing in parentDirectiveCtrl into $scope here
    link: function link(scope, element, attrs, parentDirectiveCtrl) {
      scope.parentDirectiveCtrl = parentDirectiveCtrl;
    },
    controller: 'FooController',
    controllerAs: 'controller',
    bindToController: true,
    require: '^parentDirective'
  };
});

Here I'm just using $scope to pass through parentDirectiveCtrl, which seems a little clunky.

Is there another way to access the require-ed controller from the directive's controller without the linking function?

Upvotes: 3

Views: 1090

Answers (3)

Vadim
Vadim

Reputation: 8779

There is a way to avoid using $scope to access parent controller, but you have to use link function. Angular's documentation says:

Require

Require another directive and inject its controller as the fourth argument to the linking function...

Option 1

Since controllerAs creates namespace in scope of your controller, you can access this namespace inside your link function and put required controller directly on controller of childDirective instead of using $scope. Then the code will look like this.

angular.module('app', []).
  controller('parentController', function() {
    this.doSomething = function() {
      alert('parent');
    };
  }).
  controller('childController', function() {
    this.click = function() {
      this.parentDirectiveCtrl.doSomething();
    }
  }).
  directive('parentDirective', function() {
    return {
      controller: 'parentController'
    }
  }).
  directive('childDirective', function() {
    return {
      template: '<button ng-click="controller.click()">Click me</button>',
      link: function link(scope, element, attrs, parentDirectiveCtrl) {
        scope.controller.parentDirectiveCtrl = parentDirectiveCtrl;
      },
      controller: 'childController',
      controllerAs: 'controller',
      bindToController: true,
      require: '^parentDirective'
    }
  });

Plunker:

http://plnkr.co/edit/YwakJATaeuvUV2RBDTGr?p=preview

Option 2

I usually don't use controllers in my directives at all and share functionality via services. If you don't need to mess with isolated scopes of parent and child directives, simply inject the same service to both of them and put all functionality to service.

angular.module('app', []).
  service('srv', function() {
    this.value = '';
    this.doSomething = function(source) {
      this.value = source;
    }
  }).
  directive('parentDirective', ['srv', function(srv) {
    return {
      template: '<div>' + 
                  '<span ng-click="srv.doSomething(\'parent\')">Parent {{srv.value}}</span>' +
                  '<span ng-transclude></span>' +
                '</div>',
      transclude: true,
      link: function(scope) { scope.srv = srv; }
    };
  }]).
  directive('childDirective', ['srv', function(srv) {
    return {
      template: '<button ng-click="srv.doSomething(\'child\')">Click me</button>',
      link: function link(scope) { scope.srv = srv; }
    }
  }]);

Plunker

http://plnkr.co/edit/R4zrXz2DBzyOuhugRU5U?p=preview

Upvotes: 1

superczan
superczan

Reputation: 306

Good question! Angular lets you pass "parent" controller. You already have it as a parameter on your link function. It is the fourth parameter. I named it ctrl for simplicity. You do not need the scope.parentDirectiveCtrl=parentDirectiveCtrl line that you have.

.directive('fooDirective', function () {
  return {
// Passing in parentDirectiveCtrl into $scope here
link: function link(scope, element, attrs, ctrl) {
  // What you had here is not required.
},
controller: 'FooController',
controllerAs: 'controller',
bindToController: true,
require: '^parentDirective'};});

Now on your parent controller you have

this.doSomething=function(). 

You can access this doSomething as

ctrl.doSomething().

Upvotes: 0

New Dev
New Dev

Reputation: 49590

You must use the link function to acquire the require-ed controllers, but you don't need to use the scope to pass the reference of the controller to your own. Instead, pass it directly to your own controller:

.directive('fooDirective', function () {
  return {

    require: ["fooDirective", "^parentDirective"],

    link: function link(scope, element, attrs, ctrls) {
      var me     = ctrls[0],
          parent = ctrls[1];

      me.parent = parent;
    },
    controller: function(){...},
  };
});

Be careful, though, since the controller runs prior to link, so within the controller this.parent is undefined, until after the link function runs. If you need to know exactly when that happens, you can always use a controller function to pass the parentDirective controller to:

link: function link(scope, element, attrs, ctrls) {
  //...

  me.registerParent(parent);
},
controller: function(){
  this.registerParent = function(parent){
    //...
  }
}

Upvotes: 3

Related Questions