Jared Christensen
Jared Christensen

Reputation: 525

Different ways to communication between nested directives

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?

Credit/Disclaimer: This is not my question, I found the original question on programmers.The original author never moved it over here as suggested.

Upvotes: 1

Views: 167

Answers (2)

Petr Averyanov
Petr Averyanov

Reputation: 9486

Just want to point another way for directive to interact - via objects:

<outer model='model'>
  <inner model='model[0]'></inner>
  <inner model='model[1]'></inner>
</outer>

Or more familiar case:

<input type="checkbox" ng-model="smth"/>
<div ng-show="smth"></div>

Upvotes: 0

floribon
floribon

Reputation: 19193

Thanks for pointing out to the original author.

Here are when I think each situation should be preffered:

Use require parent directive if

  • inner is always inside outer
  • inner always call the same outer API
  • inner is private, the developper interacts with outer

Use $emit event if

  • The two directives has nothing in common, especially hierarchy
  • The need for communication rely on an event
  • You feel too lazy to create a service for it

Execute expression in parent scope, via & if

  • inner doesn't need to be always inside outer
  • inner doesn't always call the same API from outer, or with the same parameters

Use scope inheritance on non-isolated scope if

Nah, don't. It is same as using require except you don't guarantee inner will be inside outer, and it become very unclear for a programmer how to use that directive.

Use a service injected into both inner and outer if

  • You are in the same situation as with the $emit case
  • But you are a good person

That's about it. A service is always better than a broadcast because it explicitely tell the programmer which events have an effect on the directive. Using $emit and the like is really the wrost option most of the time, as it all behave like the old school goto expression that many developpers love to hate: you'll have a bad time trying to debug your directives when you have too many events.

Now if the hierarchy is guaranteed and the API fixed I recommend using require as it becomes one less worry for the developper using the directives.

Upvotes: 2

Related Questions