web-dev-qa-db-fra.com

angularJS $ on ordre de déclenchement du gestionnaire d'événements

Je me demandais deux choses, dans le contexte de la gestion des événements angularJS.

  1. Comment est défini l'ordre dans lequel les gestionnaires écoutant le même événement sont déclenchés?
  2. Est-ce le signe d'un mauvais design si vous commencez à vous poser des questions à ce sujet?

Après avoir lu la documentation sur angular $ on, $ broadcast et $ emit ainsi que native flux d'événements DOM je pense que je comprends dans quel ordre les gestionnaires d'événements seront déclenchés dans différentes étendues. Le problème est que plusieurs gestionnaires écoutent dans la même étendue ($ rootScope par exemple) à partir de différents endroits (Contrôleurs vs Services par exemple).

Pour illustrer le problème, j'ai assemblé un jsfiddle avec un contrôleur et deux services, tous communiquant via $ rootScope http://jsfiddle.net/Z84tX/

Thanks

25
Renaud

Très bonne question.

Les gestionnaires d'événements sont exécutés dans l'ordre d'initialisation.

Je n'y ai pas vraiment réfléchi auparavant, car mes gestionnaires n'ont jamais eu besoin de savoir lequel exécuter en premier, mais à en juger par vous, violon, je peux voir que les gestionnaires sont appelés dans le même ordre dans lequel ils sont initialisés.

Dans votre violon, vous avez un contrôleur controllerA qui dépend de deux services, ServiceA et ServiceB:

myModule
  .controller('ControllerA', 
    [
      '$scope', 
      '$rootScope', 
      'ServiceA', 
      'ServiceB', 
      function($scope, $rootScope, ServiceA, ServiceB) {...}
    ]
  );

Les services et le contrôleur définissent un écouteur d'événements.

Maintenant, toutes les dépendances doivent être résolues avant d'être injectées, ce qui signifie que les deux services seront initialisés avant d'être injectés dans le contrôleur. Ainsi, les gestionnaires définis dans les services seront appelés en premier, car les usines de services sont initialisées avant le contrôleur.

Ensuite, vous pouvez également constater que les services sont initialisés afin d'être injectés. Donc ServiceA est initialisé avant ServiceB car ils sont injectés dans cet ordre dans le contrôleur. Si vous avez modifié leur ordre dans la signature du contrôleur, vous verrez que leur ordre d'initialisation est également modifié (ServiceB précède ServiceA).

Ainsi, une fois les services initialisés, le contrôleur est également initialisé et, avec lui, le gestionnaire d'événements défini dans.

Ainsi, le résultat final est, sur $ broadcast, les gestionnaires seront exécutés dans cet ordre: ServiceA handler, ServiceB handler, ControllerA handler.

26
Stewie

Pour répondre à # 2 (parce que je pense que la réponse de @ Stewie à # 1 est vraiment bonne), alors que j'hésite à proposer des règles concluantes qui disent: "si vous voyez cela, alors c'est du mauvais code", je proposerait de dire que si vous avez deux gestionnaires d'événements, et que l'un ne peut s'exécuter qu'après l'exécution de l'autre: vous devez évaluer pourquoi c'est le cas et si vous ne pouviez pas mieux encapsuler ou organiser la façon dont votre logique s'exécute.

L'un des principaux cas d'utilisation de la diffusion/écoute de pub/sous-événement est de permettre à des composants séparés qui sont totalement indépendants les uns des autres d'opérer sur leur domaine d'influence de manière indépendante de manière asynchrone. Si un gestionnaire ne doit fonctionner qu'après l'exécution d'un autre gestionnaire, vous supprimez la nature asynchrone de pub/sub en ajoutant une exigence secondaire (bien que cela soit peut-être nécessaire).

Si c'est une dépendance absolument nécessaire, alors non: ce n'est pas un symptôme de mauvaise conception - c'est un symptôme des exigences de cette fonctionnalité .

3
Mattygabe

C'est un peu compliqué (et je ne le recommanderais pas) de suivre cette voie, mais je voulais fournir une alternative dans le cas où vous ne pouvez pas vous assurer que serviceB sera initialisé avant serviceA et vous avez absolument besoin que l'écouteur de serviceB soit exécuté en premier.

Vous pouvez manipuler le $rootScope.$$listeners array pour placer l'écouteur de serviceB en premier.

Quelque chose comme ça fonctionnerait lors de l'ajout de l'écouteur à $rootScope sur serviceB:

var listener, listenersArray;
$rootScope.$on('$stateChangeStart', someFunction);
listenersArray = $rootScope.$$listeners.$stateChangeStart;
listener = listenersArray[listenersArray.length - 1];
listenersArray.splice(listenersArray.length - 1, 1);
listenersArray.unshift(listener);
3
kirkchris

Je suis un nouvel utilisateur de Angular JS donc veuillez pardonner si la réponse n'est pas optimale.: P

Si vous avez besoin que l'ordre des fonctions déclenchées par un événement soit dépendant (c'est-à-dire, fonction A puis fonction B), la création d'une fonction de déclenchement pourrait-elle ne pas être meilleure?

function trigger(event,data) {
    FunctionA(event,data);
    FunctionB(event,data);
}

$rootScope.on('eventTrigger',trigger);
1

Pour ajouter un autre commentaire sur le point # 2, si vous avez besoin de garantir l'ordre, vous pouvez implémenter un modèle d'observateur en utilisant un service avec un tableau d'écouteurs. Dans le service, vous pouvez définir des fonctions "addListener" qui définissent également la façon dont les écouteurs sont ordonnés. Vous pouvez ensuite injecter ce service dans tous les autres composants nécessaires pour déclencher des événements.

0
GameSalutes