简体   繁体   中英

Best way for communication between directives

Copied from here . May be, I can get more proper answer here!

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 (eg 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?

First, I want to point out that your example

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

is not going to work with

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

You either need to run the entire innerChoose expression with $parse, or you need to only pass a reference to the function, like:

<outer>
  <inner inner-choose="functionOnOuter"></inner>
  <inner inner-choose="functionOnOuter"></inner>
</outer>

Beyond that, it comes down to what makes best sense based on the stylistic preferences of your team and what you specifically need to accomplish. For example, if you need to be able to execute commands that are set up based on data in JSON that's not known in advance based on multiple layers that can be put together in different ways, you probably need to go with events, because any coupling at all could prevent you from creating or executing the right command on the right data.

If the main task that you need to do is build Views that leverage the functionality, you might want to go with a shared $scope to keep the Views as simple as possible.

I mainly just use require for ngModel, which is more of a sibling directive than parent directive. I haven't yet had a use-case for needing a reference to the entire parent controller.

When we have a hierarchy like this, I would go with the first option. require was created to couple two directives or components (from Angular 1.5) tightly. You can use this to point out that you just cannot use inner without outer .

I'm not a fan of events, because they can go through many scopes when we have a wrong usage.

& and scope settings have some advantages, but it depends on what you want. Those are not applicable for all cases.

I think nowadays in such discussions we should take Angular 2 into account. There if you want to execute a function from child component, you need to pass this component with @ViewChild annotation. I think the closest solution in Angular 1.x is to use require, which (as I've mentioned) is also implemented for Angular 1.x components.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM