Communication entre directives imbriquées

61

Il semble y avoir pas mal de façons de communiquer entre les directives. Supposons que vous ayez des directives imbriquées, où les directives internes doivent communiquer quelque chose à l'extérieur (par exemple, elles ont été choisies par l'utilisateur).

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

Jusqu'à présent, j'ai 5 façons de le faire

require: directive parent

La innerdirective peut exiger la outerdirective, qui peut exposer une méthode sur son contrôleur. Donc, dans la innerdéfinition

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

Et dans le outercontrôleur de la directive:

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

$emit un événement

La innerdirective peut constituer $emitun événement auquel elle outerpeut répondre via $on. Donc, dans le innercontrôleur de la directive:

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

et dans le outercontrôleur de directives:

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

Exécuter une expression dans l'étendue parent, via &

L'élément peut se lier à une expression de la portée parent et l'exécuter à un moment approprié. Le HTML serait comme:

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

Donc, le innercontrôleur a une fonction 'innerChoose' qu'il peut appeler

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

qui appellerait (dans ce cas) la fonction 'functionOnOuter' sur le outerchamp d'application de la directive:

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

Portée d'héritage sur une portée non isolée

Étant donné qu'il s'agit de contrôleurs imbriqués, l'héritage de la portée peut être utile et la directive interne peut simplement appeler n'importe quelle fonction de la chaîne de la portée, tant qu'elle n'a pas de portée isolée. Donc dans la innerdirective:

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

Et dans la outerdirective:

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

Par service injecté à l'intérieur et à l'extérieur

Un service peut être injecté dans les deux directives, de sorte qu'ils puissent avoir un accès direct au même objet ou appeler des fonctions pour notifier le service, et peut-être même s'enregistrer pour être notifié, dans un système de publication / sous-système. Cela n'exige pas que les directives soient imbriquées.

Question : Quels sont les inconvénients et avantages potentiels des uns par rapport aux autres?

Michal Charemza
la source
5
Je ne peux pas croire que je n'ai pas vu cette question avant maintenant. J'apprécie toutes les options que vous avez fournies. Si vous ne l'avez pas déjà fait, avez-vous pensé à poster cette question sur stackoverflow? Je m'attendrais à ce qu'il obtienne beaucoup plus de traction sur stackoverflow.
Mike Barlow - BarDev
Veuillez consulter cette proposition - softwareengineering.stackexchange.com/questions/344165/…
yellowblood

Réponses:

7

Ma préférence va à la définition d'un &attribut dans la portée de la directive, principalement parce que je considère la scope: {}définition d'une directive comme son API. Il est beaucoup plus facile d'examiner une définition d'attribut de portée pour voir les informations nécessaires au bon fonctionnement de la directive que de rechercher des fonctions de liaison et de contrôleur pour $emitles événements d, les fonctions de portée héritées ou utilisées dans les contrôleurs injectés.

Jeff Swensen
la source
1

Mon avis:

Les services constituent le moyen privilégié de partager le comportement / les données entre modules, directives et contrôleurs. Les directives sont des éléments isolés pouvant être imbriqués ou non. Les contrôleurs doivent rester autant que possible un modèle de vue. Dans l'idéal, aucune logique métier ne devrait s'y retrouver.

Alors:

Lorsque vous commencez à les lier ensemble en accédant aux fonctions d'étendue parent, je pense que vous risquez de les coupler beaucoup trop fort et de rendre toute l'application illisible et les composants non réutilisables. Lorsque vous découplez ces données ou comportements partagés dans un service, vous avez l'avantage de réutiliser l'intégralité des directives avec des données / comportements différents, même en déterminant le service à utiliser au moment de l'exécution. C’est en quoi consiste l’injection de dépendance.

RobbyD
la source