Comment encapsuler les événements uniques et temporels dans un service?

J'essaie d'encapsuler les événements dans un service afin de mettre en place une mécanique pour vous abonner / désabonner les auditeurs lorsque la portée d'un contrôleur est détruite. Ceci parce que j'ai utilisé le fichier racine. $ On de la manière suivante:

if(!$rootScope.$$listeners['event']) { $rootScope.$on('event', function(ev, data){ // do some... }); } 

ou

 $scope.$on('$destroy', function(ev, data){ // unsubscribe the listener }); 

Donc, j'ai juste besoin d'un auditeur de cet événement, j'ai besoin de supprimer l'auditeur existant lorsque le contrôleur n'est plus en vie, car la fonction que j'ai enregistrée plus tôt est toujours déclenchée.

J'ai donc besoin de mettre en place un auditeur d'événement $ destroy sur mon contrôleur, pour détruire l'auditeur lorsque la portée est détruite, mais je ne veux pas faire ce code chaque fois que je crée un événement. C'est pourquoi je veux créer un service dans lequel je vais encapsuler les événements.

 angular.module('core').factory('event', [ function() { var service = {}; service.events = {}; service.on = function(scope, eventId, callback) { scope.$on('$destroy', function(ev, other){ //unsubscribe }); service.events[eventId] = callback; // scope = null; I guess ? }; service.emit = function(eventId, data){ if (service.events[eventId]) service.events[eventId](data); else return new Error('The event is not subscribed'); }; return service; } ]); 

Cela pourrait être fait en utilisant $ rootScope au lieu de mes propres méthodes, mais encapsulant $ on et $ emit de $ rootScope , mais à la fin, j'aurai le même problème ici.

Ce sont donc mes questions:

  1. Est-ce une bonne pratique de passer la valeur de référence de la portée à un service?
  2. Quel est le sens de $$ détruit? Quand cela est vrai signifie que angularJS n'a pas de références internes à l'instance?
  3. Devrais-je faire un scope = null à mon service pour permettre à GC de supprimer l'objet ou angularJS gère-t-il une suppression explicite?
  4. Y a-t-il une meilleure façon de faire ce que je veux?

Ce que vous essayez d'accomplir, c'est essentiellement un autobus événementiel .
Vous avez également décrit très bien ce qui ne va pas avec la mise en œuvre actuelle. Une façon différente d'aborder le problème est de décorer le $ rootScope avec votre bus (ou tout autre bus d'événement pour cette question). Voici comment:

 app.config(function ($provide) { $provide.decorator('$rootScope', ['$delegate', '$$bus', function ($delegate, $$bus) { Object.defineProperty($delegate.constructor.prototype, '$bus', { get: function () { var self = this; return { subscribe: function () { var sub = $$bus.subscribe.apply($$bus, arguments); self.$on('$destroy', function () { console.log("unsubscribe!"); sub.unsubscribe(); }); }, publish: $$bus.publish }; }, enumerable: false }); return $delegate; }]); }); 

Compte tenu de l'implémentation de bus $$ suivante (maintenue basique pour la simplicité):

 app.factory('$$bus', function () { var api = {}; var events = {}; api.subscribe = function (event) { if (!events.hasOwnProperty(event.name)) { events[event.name] = [event]; } else { events[event.name].push(event); } return { unsubscribe: function () { api.unsubscribe(event); } } }; api.publish = function (eventName, data) { if (events.hasOwnProperty(eventName)) { console.log(eventName); angular.forEach(events[eventName], function (subscriber) { subscriber.callback.call(this, data); }); } }; api.unsubscribe = function (event) { if (events.hasOwnProperty(event.name)) { events[event.name].splice(events[event.name].indexOf(event), 1); if (events[event.name].length == 0) { delete events[event.name]; } } }; return api; }); 

Maintenant, tout ce que vous avez à faire est de s'inscrire ou de publier des événements. La désinscription aura lieu automatiquement (lorsque la portée de $ est détruite):

  $scope.$bus.subscribe({ name: 'test', callback: function (data) { console.log(data); } }); 

Et plus tard, publiez un événement:

  $scope.$bus.publish('test', {name: "publishing event!"}); 

Un point important à faire est que les événements eux-mêmes sont souscrits à chaque variable individuelle et non sur $ rootScope. C'est ainsi que vous «savez» la portée de $ à libérer.

Je pense que cela répond à votre question. Dans cet esprit, vous pouvez évidemment rendre ce mécanisme très sophistiqué (par exemple, l'auditeur d'événements de contrôleur lancé lorsqu'une vue est acheminée, vous désabonnnez automatiquement uniquement à certains événements, etc.). Bonne chance!

** Cette solution est prise en compte ici, qui utilise un cadre de bus différent (autrement qu'il est identique).