globalna komunikacja w module angular: szyna zdarzeń lub wzór/usługa mediatora
Do tej pory widziałem wiele rozwiązań tego problemu. Najprostszym jest oczywiście $emit
Zdarzenie w $rootScope
jako szyna zdarzeń np. ( 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');
});
}
}
});
A innym jest zadeklarowanie usługi z mediatorem lub funkcjonalnością pubsub / zamkniętym zakresem np. (komunikowanie się między wieloma kontrolerami i dyrektywą . )
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;
}
);
Pytanie brzmi:
- czy jest sens aby użyć drugiego w aplikacji kątowej?
- a jakie są plusy i minusy każdego z nich w porównaniu do siebie?
3 answers
Tworzenie własnej implementacji emitera zdarzeń jest nieproduktywne przy pisaniu aplikacji AngularJS. Angular dostarcza już wszystkie narzędzia potrzebne do komunikacji opartej na zdarzeniach.
- używanie
$emit
Na$rootScope
dobrze sprawdza się w globalnej komunikacji międzyserwisowej i tak naprawdę nie ma żadnych wad. - użycie
$broadcast
na naturalnym zakresie (takim, który jest związany z częścią DOM) zapewnia komunikację między komponentami widoku (dyrektywami, Kontrolery). - użycie
$broadcast
na$rootScope
łączy dwa poprzednie punkty (zapewnia całkowicie globalną platformę komunikacyjną). jest to rozwiązanie używane w zasadzie przez każdą bibliotekę opartą na AngularJS.
I
- Jeśli martwisz się o wydajność poprzedniej opcji i naprawdę chcesz mieć oddzielny emiter zdarzeń, możesz łatwo go utworzyć, tworząc izolowany zakres (
$rootScope.$new(true)
) i używając$broadcast
na nim. (Można go następnie owinąć w usługę i wstrzyknąć go w dowolne miejsce.)
Ostatnia opcja tworzy pełnoprawny emiter zdarzeń zintegrowany z Angular (implementacja podana w twoim pytaniu musiałaby przynajmniej zawinąć wszystkie wywołania słuchacza w $apply()
, aby poprawnie zintegrować) , które mogą być dodatkowo wykorzystane do obserwacji zmian danych, jeśli pasuje do konkretnego przypadku użycia.
Jednak, o ile Twoja aplikacja nie jest naprawdę ogromna, lub jesteś naprawdę paranoikiem na temat kolizji nazw zdarzeń, pierwsze trzy opcje powinny wystarczyć.
Nie będę wdawać się w szczegóły o innych środkach komunikacji między twoimi komponentami. Ogólnie rzecz biorąc, gdy sytuacja wymaga udostępniania danych za pomocą zakresu, bezpośredniej interakcji kontrolerów lub komunikacji za pomocą atrybutów węzła DOM, powinieneś o tym wiedzieć.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-29 17:37:24
[[3]}powiedziałbym, że nadawanie jest kanciastym sposobem, jak to osiągnąć.
Jednak twój mediator może działać, jeśli przekażesz wewnętrzną funkcję dyrektywy, w przykładzie użyłem metody na scope, ale można to zrobić również metodą kontrolera.
Użyłem dokładnie tej samej fabryki, Co ty.
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);
}
};
});
Pełny przykład: http://jsbin.com/mobifuketi/1/edit?html, js, console, output
Aby być kompletnym, chciałbym dodać, że angular zapewnia również więcej możliwość komunikowania się dyrektyw.
Require atribute
Jeśli Twoje dyrektywy są połączone w hierarchii, możesz użyć atrybutu require, który pozwala ci uzyskać dostęp do innego kontrolera dyrektyw. Jest to zwykle najlepsze rozwiązanie w wielu przypadkach.
.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");
};
}
};
});
Pełny przykład: http://jsbin.com/turoxikute/1/edit?html, js, console, output
Using $watch
Jeśli funkcjonalność zależy od danych, a nie od akcji, korzystasz z $watch i react na zmiany danego modelu lub modelu przechowywanego w usłudze shared service, nie jest podobny do listenera, jego podstawowe sprawdzanie zmian. Nazwałem metodę changeState () i log "state changed", aby wszyscy widzieli to wyraźnie.
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");
});
}
};
});
Pełny przykład: http://jsbin.com/darefijeto/1/edit?html, js, console, output
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-10 20:34:24
Lubię autobus eventowy.
Angular dostarcza $emit na $rootScope, ale nie sądzę, że powinno to wiązać twoją decyzję o użyciu go do przepływów opartych na zdarzeniach, jeśli są one złożone lub przewidywalnie złożone. Angular ma wiele funkcji i chociaż większość z nich jest świetna, nawet autorzy przyznają, że mają one głównie uzupełniać dobre zasady inżynierii oprogramowania, a nie je zastępować.
Lubię ten post na using postal.js: kątowy.js event bus z pocztą.js . The two głównymi korzyściami są kanały i koperty, które pozwolą na bardziej jednoznaczną, zrozumiałą i elastyczną logikę opartą na zdarzeniach.
Uważam, że podejścia oparte na usługach są podatne na błędy, jeśli stan nie jest zarządzany ściśle, co jest trudne w przypadku wywołań asynchronicznych i zastrzyków, gdzie nie można być pewnym, w jaki sposób usługa będzie wielofunkcyjna w przyszłości.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-28 16:25:06