60

There seem to be quite a few ways of communicating between directives. Say you have nested directives, where the inner directives must communicate something to the outer (e.g. it's been chosen by the user).

<outer>
  <inner></inner>
  <inner></inner>
</outer>

So far I have 5 ways of doing this

require: parent directive

The inner directive can require the outer directive, which can expose some method on its controller. So in the inner definition

require: '^outer',
link: function(scope, iElement, iAttrs, outerController) {
   // This can be passed to ng-click in the template
   $scope.chosen = function() {
     outerController.chosen(something);
   }
}

And in the outer directive's controller:

controller: function($scope) {
   this.chosen = function(something) {
   }
}

$emit event

The inner directive can $emit an event, which the outer directive can respond to, via $on. So in the inner directive's controller:

controller: function($scope) {
  $scope.chosen = function() {
    $scope.$emit('inner::chosen', something);
  }
}

and in the outer directives controller:

controller: function($scope) {
  $scope.$on('inner::chosen, function(e, data) {
  }
}

Execute expression in parent scope, via &

The item can bind to an expression in the parent scope, and execute it at an appropriate point. The HTML would be like:

<outer>
  <inner inner-choose="functionOnOuter(item)"></inner>
  <inner inner-choose="functionOnOuter(item)"></inner>
</outer>

So the inner controller has an 'innerChoose' function it can call

scope: {
  'innerChoose': '&'
},
controller: function() {
  $scope.click = function() {
    $scope.innerChoose({item:something});
  }
}

which would call (in this case) the 'functionOnOuter' function on the outer directive's scope:

controller: function($scope) {
  $scope.functionOnOuter = function(item) {
  }
}

Scope inheritance on non-isolated scope

Given that these are nested controllers, scope inheritance can be at work, and the inner directive can just call any functions in the scope chain, as long as it doesn't have an isolated scope). So in the inner directive:

// scope: anything but a hash {}
controller: function() {
  $scope.click = function() {
    $scope.functionOnOuter(something);
  }
}

And in the outer directive:

controller: function($scope) {
  $scope.functionOnOuter = function(item) {
  }
}

By service injected into both inner and outer

A service can be injected into both directives, so they can have direct access to the same object, or call functions to notify the service, and maybe even register themselves to be notified, in a pub/sub system. This doesn't require the directives to be nested.

Question: What are any potential drawbacks and advantages of each over the others?

2
  • 5
    I can't believe I haven't seen this question before now. I appreciate all the options that you provided. If you haven't already done so, have you thought of posting this question on stackoverflow? I would expect that it would get much more traction on stackoverflow. Commented Jul 9, 2014 at 15:38
  • Please see this proposal - softwareengineering.stackexchange.com/questions/344165/… Commented Mar 15, 2017 at 12:05

2 Answers 2

7

My preference is for defining a & attribute in the directive scope primarily because I view the scope: {} definition of a directive as its API. It's much easier to look at a scope attribute definition to see what information the directive needs to function properly than it is to scour link and controller functions for $emit'd events, inherited scope functions or functions used within injected controllers.

1

My opinion:

Services are the preferred way of sharing behaviour/data across modules/directives/controllers. Directives are isolated things that can be nested or not. Controllers should stick to being a viewmodel as much as they can, ideally no business logic should end up in there.

So:

When you start wiring them together by accessing parent scope functions I think you risk coupling them way too hard and making the entire application unreadable and components not reusable. When you decouple that shared data or behaviour in a service you have the benefit of reusing the entire directives with different data/behaviour even determining the service to be used at runtime. Which is what dependency injection is all about.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.