6

This is more of an organizational approach to solving this issue rather than a direct solution. My question itself is that if I have two directives which are not dependent on each other and can both work independently to serve their purposes. But if one of the directives is present then the other one needs to execute once the other is ready. In this case then what would be the logical way to make sure that it works out that way without the need to hardcode any function calls or events?

Lets say for example you have one directive which builds a grid of some sort:

angular.module('App').directive('appGrid',function() {
  return function($scope, element) {
    $scope.rows = ...
  };
});

Then I have another directive that makes the element horizontally scrollable:

angular.module('App').directive('appPane',function() {
  return function($scope, element) {
    element.attachHorizontalScroll();
  };
});

So an example of my HTML would look like this:

<div data-app-grid data-app-pane>
  <div data-ng-repeat="row in rows">
    <div data-ng-repeat="cell in row.cells">
      {{ cell.data }}
    </div>
  </div>
</div>

Basically the appPane directive needs to run after the appGrid directive has been executed and the table is ready.

One solution I can think of is to watch the data to see when it is ready using the $scope.$watch method, but this poses a problem since the change can occur multiple times and this would be bad design to redundantly update the page and it also poses a problem if multiple directives are writing to the same scope variable that is being watched.

Another solution is to have the first directive emit an event (something like elementReady) and then have the 2nd directive take over. But what about if the first directive isn't there? Then how would the 2nd directive know when to do it's job? There could be another directive which is basically an empty directive which just fires the event for all other elements, but this is a bit of hack. Also what happens if multiple other directives fire the elementReady event?

One more solution is to create a 3rd directive which shares the logic between the two directives via a shared service. But this makes the 3rd directive fully reliant on both other directives as well as the shared services in between. This also require more, unnecessary testing code as well as actual code to write the directive (much more code compared to the 2nd solution, which would require only one + one lines of code).

Any ideas?

C1pher
  • 1,933
  • 6
  • 33
  • 52
matsko
  • 21,895
  • 21
  • 102
  • 144

3 Answers3

3

Have a look at the priority attribute of the directives.

Here is a copy of the exact description from the angular docs :

priority - When there are multiple directives defined on a single DOM element, sometimes it is necessary to specify the order in which the directives are applied. The priority is used to sort the directives before their compile functions get called. Higher priority goes first. The order of directives within the same priority is undefined.

you should be able to find it in

http://docs.angularjs.org/guide/directive

under the Writing directives (long version) --- Directives definition Object section.

Hope this answers your question.

ganaraj
  • 26,841
  • 6
  • 63
  • 59
  • Yeah I was thinking that the priority attribute could work although I had never used it. I will take a look. Thanks :) – matsko Nov 30 '12 at 14:45
3

I had a similar problem. I couldn't use priority, since the wiring occurred after clicking on the element. I solved it using $rootScope. Here is a simplified example:

link : function (scope, element, attrs) {
   element.on('click', function() {
       $rootScope.$emit('myEvent', myData);
   });
}

In the other directive you 'listen' for myEvent:

link : function (scope, element, attrs) {
   $rootScope.$on('myEvent', function(data) {
      // do sth
   });
}
asgoth
  • 35,552
  • 12
  • 89
  • 98
2

Great question. I would use a combination of attribute(s) and event(s).

Since only the user of the directives (i.e., the person writing the HTML) knows that he/she wants the two directives to interact (i.e., run dependently), I think he/she needs to specify this somehow, and attributes seem like a good way (the only way?). Once the directives know they need to interact, they can use events to do the signaling.

So, if directive B needs to wait for directive A, one or more attributes can be used to specify who should do the waiting, who should fire an event, and/or what the event name is. (This of course can be expanded to more than two directives, and more than one event.) Some possible implementations:

<div data-app-grid data-app-pane idc-wait="appPane" idc-event="idc-appGridDone">
<div data-app-grid data-app-pane idc-wait="appPane" idc-emit="appGrid" idc-event="idc-appGridDone">

By examining the attributes, the appGrid directive can determine that it doesn't need to wait, but it does need to emit event "idc-appGridDone". Similarly, by examining the attributes, the appPane directive can determine that it needs to wait for an "idc-appGridDone" event before it runs.

If the directives don't find any of these special "inter-directive communication"/"idc-" attributes, they run normally (no events, no waiting).

The "(in)dependent inter-directive communication pattern" is born. ☺

Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492