javascript - tag - comunicación global en módulo angular: bus de evento o patrón/servicio de mediador
title of page html (3)
Hasta ahora he visto muchas soluciones al problema. El más simple es, por supuesto, $emit
un evento en $rootScope
como un bus de eventos, por ejemplo ( https://github.com/btilford/anti-patterns/blob/master/angular/Angular.md )
angular.module(''myModule'').directive(''directiveA'', function($rootScope) {
return {
link : function($scope, $element) {
$element.on(''click'', function(event) {
$rootScope.$emit(''directiveA:clicked'', event);
});
}
}
});
angular.module(''myModule'').directive(''directiveB'', function() {
return {
link : function($scope, $element) {
$rootScope.on(''directiveA:clicked'', function(event) {
console.log(''received click event from directiveA'');
});
}
}
});
y otro es declarar un servicio con un mediador o una funcionalidad pubsub / un alcance cerrado, por ejemplo ( Comunicación entre múltiples controladores y una directiva ).
module.factory(''MessageService'',
function() {
var MessageService = {};
var listeners = {};
var count = 0;
MessageService.registerListener = function(listener) {
listeners[count] = listener;
count++;
return (function(currentCount) {
return function() {
delete listeners[currentCount];
}
})(count);
}
MessageService.broadcastMessage = function(message) {
var keys = Object.keys(listeners);
for (var i = 0; i < keys.length; i++) {
listeners[keys[i]](message);
}
}
return MessageService;
}
);
La pregunta es:
- ¿Hay algún punto para usar el segundo en una aplicación angular?
- y ¿cuáles son los pros y los contras de cada uno de ellos en comparación el uno con el otro?
Diría que la transmisión es una forma angular de cómo lograr esto.
Sin embargo, su mediador puede funcionar, si pasa la función interna de la directiva, en el ejemplo he usado el método en el alcance, pero también se puede hacer con el método del controlador.
He usado exactamente la misma fábrica que publicas.
angular.module("sharedService", [])
.factory(''MessageService'',
function() {
var MessageService = {};
var listeners = {};
var count = 0;
MessageService.registerListener = function(listener) {
listeners[count] = listener;
count++;
return (function(currentCount) {
return function() {
delete listeners[currentCount];
};
})(count);
};
MessageService.broadcastMessage = function(message) {
var keys = Object.keys(listeners);
for (var i = 0; i < keys.length; i++) {
listeners[keys[i]](message);
}
};
return MessageService;
}
)
.directive("directiveA", function(MessageService) {
return {
link:function(scope) {
scope.click = function() {
MessageService.broadcastMessage("broadcasted message");
};
},
template: ''<button ng-click="click()">Click</button>''
};
})
.directive("directiveB", function(MessageService) {
return {
link:function(scope) {
scope.callback = function(message) {
console.log(message);
};
MessageService.registerListener(scope.callback);
}
};
});
Ejemplo completo: http://jsbin.com/mobifuketi/1/edit?html,js,console,output
Solo para completar, me gustaría agregar, que angular también proporciona más posibilidades de cómo se pueden comunicar las directivas.
Requerir atributo
Si sus directivas están conectadas en jerarquía, puede usar el atributo require que le permite acceder al controlador de otras directivas. Esta suele ser la mejor solución para muchos casos.
.directive("directiveA", function() {
return {
require: "^directiveB",
link: function(scope, element, attrs, directiveCtrl) {
scope.click = function() {
directiveCtrl.call();
};
},
template: ''<button ng-click="click()">Click</button>''
};
})
.directive("directiveB", function() {
return {
controller :function() {
this.call = function() {
console.log("method has been called");
};
}
};
});
Ejemplo completo: http://jsbin.com/turoxikute/1/edit?html,js,console,output
Usando $ reloj
Si la funcionalidad depende de los datos y no de la acción, usted utiliza $ watch y reacciona a los cambios del modelo o modelo dado almacenados en el servicio compartido, no es como el oyente, es básicamente la comprobación del cambio. He nombrado método changeState () y registro "estado cambiado" para que todos lo vean claro.
angular.module("sharedService", [])
.service("MediatorService", function() {
this.state = true;
this.changeState = function() {
this.state = !this.state;
};
})
.directive("directiveA", function(MediatorService) {
return {
link:function(scope) {
scope.click = function() {
MediatorService.changeState();
};
},
template: ''<button ng-click="click()">Click</button>''
};
})
.directive("directiveB", function(MediatorService) {
return {
link:function(scope) {
scope.mediator = MediatorService;
scope.$watch("mediator.state", function(oldValue, newValue) {
if (oldValue == newValue) {
return;
}
console.log("state changed");
});
}
};
});
Ejemplo completo: http://jsbin.com/darefijeto/1/edit?html,js,console,output
La creación de su propia implementación del emisor de eventos es contraproducente cuando se escribe una aplicación AngularJS. Angular ya proporciona todas las herramientas necesarias para la comunicación basada en eventos.
- Usar
$emit
en$rootScope
funciona muy bien para la comunicación global entre servicios y no tiene ningún inconveniente. - El uso de
$broadcast
en un ámbito natural (uno que está vinculado a una parte de su DOM) proporciona una comunicación de ámbito entre los componentes de visualización (directivas, controladores). - Usar
$broadcast
en$rootScope
reúne los dos puntos anteriores (proporciona una plataforma de comunicación completamente global). Esta es la solución utilizada básicamente por cualquier biblioteca basada en AngularJS.
y
- Si le preocupa el rendimiento en la opción anterior y realmente desea su emisor de eventos por separado, puede crear uno fácilmente creando un alcance aislado (
$rootScope.$new(true)
) y usando$broadcast
en él. (Luego puede envolverlo en un servicio e inyectarlo en cualquier lugar que desee).
La última opción crea un emisor de eventos completo integrado en Angular (la implementación provista en su pregunta al menos necesitaría ajustar todas las llamadas de escucha en $apply()
para integrarse adecuadamente) que se puede usar adicionalmente para la observación de cambio de datos, si eso se adapta a un caso de uso particular.
Sin embargo, a menos que su aplicación sea realmente enorme, o que esté realmente paranoico con respecto a las colisiones de nombres de eventos, las tres primeras opciones deberían ser suficientes.
No entraré en detalles sobre otros medios de comunicación entre sus componentes. En términos generales, cuando la situación requiere compartir datos utilizando el alcance, la interacción directa de los controladores o la comunicación a través de los atributos del nodo DOM, debe saberlo.
Me gusta un autobús de eventos.
Angular proporciona $ emitir en $ rootScope, pero no creo que deba vincular su decisión de usarlo para flujos basados en eventos si son complejos o previsiblemente complejos. Angular tiene muchas características y, aunque la mayoría son geniales, incluso los autores admiten que, en su mayoría, están destinadas a complementar los buenos principios de ingeniería de software, no a reemplazarlos.
Me gusta esta publicación sobre el uso de postal.js : un bus de eventos angular.js con postal.js . Los dos principales beneficios son los canales y las envolventes, que permitirán una lógica más explícita, comprensible y flexible basada en eventos.
Encuentro que los enfoques basados en servicios son propensos a errores si el estado no se gestiona con fuerza, lo cual es difícil con las llamadas e inyecciones asincrónicas, donde no se puede estar seguro de cómo un servicio tendrá múltiples propósitos en el futuro.