Jaki jest prawidłowy sposób komunikacji między kontrolerami w AngularJS?
Jaki jest prawidłowy sposób komunikacji między kontrolerami?
Obecnie używam okropnej krówki z udziałem window
:
function StockSubgroupCtrl($scope, $http) {
$scope.subgroups = [];
$scope.handleSubgroupsLoaded = function(data, status) {
$scope.subgroups = data;
}
$scope.fetch = function(prod_grp) {
$http.get('/api/stock/groups/' + prod_grp + '/subgroups/').success($scope.handleSubgroupsLoaded);
}
window.fetchStockSubgroups = $scope.fetch;
}
function StockGroupCtrl($scope, $http) {
...
$scope.select = function(prod_grp) {
$scope.selectedGroup = prod_grp;
window.fetchStockSubgroups(prod_grp);
}
}
19 answers
Edit : problem rozwiązany w tej odpowiedzi został rozwiązany w trybie angular.js Wersja 1.2.7 . $broadcast
Teraz unika bulgotania przez niezarejestrowane zakresy i działa tak szybko jak $emit.
Więc teraz możesz:
- użyj
$broadcast
z$rootScope
- listen using
$on
z lokalnego$scope
to musi wiedzieć o wydarzeniu
Oryginalna Odpowiedź Poniżej
Radzę nie używać $rootScope.$broadcast
+ $scope.$on
ale raczej $rootScope.$emit
+ $rootScope.$on
. Te pierwsze mogą powodować poważne problemy z wydajnością, jak podniósł @numan. Dzieje się tak dlatego, że zdarzenie przejdzie przez wszystkie zakresy.
Jednak ten ostatni (za pomocą $rootScope.$emit
+ $rootScope.$on
) czyNie cierpią z tego powodu i dlatego może być używany jako kanał szybkiej komunikacji!
Z dokumentacji kątowej $emit
:
Wysyła nazwę zdarzenia w górę poprzez hierarchię zakresu powiadamiając o zarejestrowany
Ponieważ nie ma zakresu powyżej $rootScope
, nie ma bąbelków. Jest całkowicie bezpieczny w użyciu $rootScope.$emit()
/ $rootScope.$on()
jako EventBus.
Jest jednak jeden, gdy używa się go z kontrolerów. Jeśli bezpośrednio połączysz się z $rootScope.$on()
z kontrolera, będziesz musiał sam wyczyścić Wiązanie, gdy twój lokalny $scope
zostanie zniszczony. Wynika to z faktu, że kontrolery (w przeciwieństwie do usług) mogą być uruchamiane wielokrotnie w ciągu życia aplikacja, która skutkowałaby wiązaniami podsumowującymi ostatecznie tworzenie wycieków pamięci w całym miejscu:)
Aby się wyrejestrować, wystarczy nasłuchać zdarzenia $scope
's $destroy
, a następnie wywołać funkcję, która została zwrócona przez $rootScope.$on
.
angular
.module('MyApp')
.controller('MyController', ['$scope', '$rootScope', function MyController($scope, $rootScope) {
var unbind = $rootScope.$on('someComponent.someCrazyEvent', function(){
console.log('foo');
});
$scope.$on('$destroy', unbind);
}
]);
Powiedziałbym, że to nie jest tak naprawdę kątowa rzecz, ponieważ dotyczy również innych implementacji EventBus, że trzeba oczyścić zasoby.
Jednak Ty możesz ułatwić sobie życie w tych sprawach. Na przykład, można małpie patch$rootScope
i dać mu $onRootScope
, który subskrybuje zdarzenia emitowane na $rootScope
, ale także bezpośrednio czyści obsługę, gdy lokalny $scope
zostanie zniszczony.
Najczystszym sposobem na łatanie $rootScope
, aby zapewnić taką metodę $onRootScope
, byłoby użycie dekoratora (blok run prawdopodobnie zrobi to dobrze, ale pssst, nie mów nikomu)
Aby upewnić się, że właściwość $onRootScope
nie pojawi się nieoczekiwanie podczas wyliczania nad $scope
używamy Object.defineProperty()
i ustawić enumerable
na false
. Pamiętaj, że możesz potrzebować podkładki ES5.
angular
.module('MyApp')
.config(['$provide', function($provide){
$provide.decorator('$rootScope', ['$delegate', function($delegate){
Object.defineProperty($delegate.constructor.prototype, '$onRootScope', {
value: function(name, listener){
var unsubscribe = $delegate.$on(name, listener);
this.$on('$destroy', unsubscribe);
return unsubscribe;
},
enumerable: false
});
return $delegate;
}]);
}]);
Z tą metodą kod kontrolera z góry można uprościć do:
angular
.module('MyApp')
.controller('MyController', ['$scope', function MyController($scope) {
$scope.$onRootScope('someComponent.someCrazyEvent', function(){
console.log('foo');
});
}
]);
Więc jako ostateczny wynik tego wszystkiego gorąco radzę użyć $rootScope.$emit
+ $scope.$onRootScope
.
Btw, staram się przekonać zespół angular do rozwiązania problemu wewnątrz angular core. Tutaj toczy się dyskusja: https://github.com/angular/angular.js/issues/4574
Oto jsperf to pokazuje, jak wiele perf impact $broadcast
przynosi do tabeli w przyzwoitym scenariuszu z zaledwie 100 $scope
'S. {44]}
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-07-20 22:43:09
The top answer tutaj było obejście problemu kątowego, który już nie istnieje (przynajmniej w wersjach >1.2.16 i "prawdopodobnie wcześniej"), jak wspomniał @zumalifeguard. Ale pozostaje mi czytać te wszystkie odpowiedzi bez rzeczywistego rozwiązania.
Wydaje mi się, że teraz odpowiedź powinna brzmieć
- użyj
$broadcast
z$rootScope
- listen using
$on
z lokalnego$scope
to musi wiedzieć o wydarzeniu
Więc do publikuj
// EXAMPLE PUBLISHER
angular.module('test').controller('CtrlPublish', ['$rootScope', '$scope',
function ($rootScope, $scope) {
$rootScope.$broadcast('topic', 'message');
}]);
I subskrybuj
// EXAMPLE SUBSCRIBER
angular.module('test').controller('ctrlSubscribe', ['$scope',
function ($scope) {
$scope.$on('topic', function (event, arg) {
$scope.receiver = 'got your ' + arg;
});
}]);
Plunkers
- zwykła składnia $ scope (Jak widać powyżej)
-
Nowa
Controller As
składnia
Jeśli zarejestrujesz słuchacz na lokalnym $scope
, zostanie on zniszczony automatycznie przez $destroy
, gdy skojarzony kontroler zostanie usunięty.
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
2017-05-23 11:54:57
Using $rootScope.$broadcast oraz $scope.$on za komunikację PubSub.
Zobacz też ten post: AngularJS-komunikacja między kontrolerami
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
2013-09-05 16:36:50
Ponieważ defineProperty ma problem ze zgodnością przeglądarki, myślę, że możemy pomyśleć o użyciu usługi.
angular.module('myservice', [], function($provide) {
$provide.factory('msgBus', ['$rootScope', function($rootScope) {
var msgBus = {};
msgBus.emitMsg = function(msg) {
$rootScope.$emit(msg);
};
msgBus.onMsg = function(msg, scope, func) {
var unbind = $rootScope.$on(msg, func);
scope.$on('$destroy', unbind);
};
return msgBus;
}]);
});
I użyj go w kontrolerze tak:
-
Kontroler 1
function($scope, msgBus) { $scope.sendmsg = function() { msgBus.emitMsg('somemsg') } }
-
Controller 2
function($scope, msgBus) { msgBus.onMsg('somemsg', $scope, function() { // your logic }); }
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-02-17 03:30:50
GridLinked opublikował rozwiązanie PubSub, które wydaje się być całkiem dobrze zaprojektowane. Serwis można znaleźć, tutaj.
Również schemat ich obsługi:
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
2012-07-10 20:47:26
W rzeczywistości korzystanie z emit i broadcast jest nieefektywne, ponieważ zdarzenie pęka w górę iw dół hierarchii zakresu, co może łatwo obniżyć się do poziomu wydajności dla złożonej aplikacji.
Proponuję skorzystać z usługi. Oto jak ostatnio zaimplementowałem go w jednym z moich projektów - https://gist.github.com/3384419 .
Podstawowy pomysł-zarejestruj autobus pubsub/event jako usługę. Następnie wprowadź ten eventbus tam, gdzie chcesz subskrybować lub opublikować wydarzenia/tematy.
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
2012-08-18 04:30:08
Używając metod get I set w usłudze możesz bardzo łatwo przesyłać wiadomości między kontrolerami.
var myApp = angular.module("myApp",[]);
myApp.factory('myFactoryService',function(){
var data="";
return{
setData:function(str){
data = str;
},
getData:function(){
return data;
}
}
})
myApp.controller('FirstController',function($scope,myFactoryService){
myFactoryService.setData("Im am set in first controller");
});
myApp.controller('SecondController',function($scope,myFactoryService){
$scope.rslt = myFactoryService.getData();
});
W HTML HTML możesz sprawdzić TAK
<div ng-controller='FirstController'>
</div>
<div ng-controller='SecondController'>
{{rslt}}
</div>
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-08-02 03:03:37
Odnośnie oryginalnego kodu-wygląda na to, że chcesz udostępnić dane między zakresami. Aby udostępnić dane lub stan pomiędzy $scope, docs sugeruje użycie usługi:
- aby uruchomić kod bezstanowy lub Stanowy współdzielony między kontrolerami-użyj usługi kątowe.
- aby utworzyć lub zarządzać cyklem życia inne komponenty(np. do tworzenia instancji usługi).
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-12-24 03:54:32
Zacząłem używać poczty.js jako magistrala komunikatów pomiędzy kontrolerami.
Istnieje wiele korzyści z tego, jak szyna komunikatów, takich jak wiązania w stylu AMQP, sposób, w jaki postal może integrować się z ramkami iFrames i gniazdami sieciowymi i wiele innych rzeczy.
Użyłem dekoratora, aby ustawić pocztę na $scope.$bus
...
angular.module('MyApp')
.config(function ($provide) {
$provide.decorator('$rootScope', ['$delegate', function ($delegate) {
Object.defineProperty($delegate.constructor.prototype, '$bus', {
get: function() {
var self = this;
return {
subscribe: function() {
var sub = postal.subscribe.apply(postal, arguments);
self.$on('$destroy',
function() {
sub.unsubscribe();
});
},
channel: postal.channel,
publish: postal.publish
};
},
enumerable: false
});
return $delegate;
}]);
});
Oto link do wpisu na blogu w temacie...
http://jonathancreamer.com/an-angular-event-bus-with-postal-js/
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-05-07 22:55:31
Tak to robię z Factory / Services i simple dependency injection (DI) .
myApp = angular.module('myApp', [])
# PeopleService holds the "data".
angular.module('myApp').factory 'PeopleService', ()->
[
{name: "Jack"}
]
# Controller where PeopleService is injected
angular.module('myApp').controller 'PersonFormCtrl', ['$scope','PeopleService', ($scope, PeopleService)->
$scope.people = PeopleService
$scope.person = {}
$scope.add = (person)->
# Simply push some data to service
PeopleService.push angular.copy(person)
]
# ... and again consume it in another controller somewhere...
angular.module('myApp').controller 'PeopleListCtrl', ['$scope','PeopleService', ($scope, PeopleService)->
$scope.people = PeopleService
]
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-07-31 09:37:44
Podobał mi się sposób, w jaki $rootscope.emit
został użyty do osiągnięcia komunikacji międzykomunikacyjnej. Proponuję czyste i wydajne rozwiązanie bez zanieczyszczania globalnej przestrzeni.
module.factory("eventBus",function (){
var obj = {};
obj.handlers = {};
obj.registerEvent = function (eventName,handler){
if(typeof this.handlers[eventName] == 'undefined'){
this.handlers[eventName] = [];
}
this.handlers[eventName].push(handler);
}
obj.fireEvent = function (eventName,objData){
if(this.handlers[eventName]){
for(var i=0;i<this.handlers[eventName].length;i++){
this.handlers[eventName][i](objData);
}
}
}
return obj;
})
//Usage:
//In controller 1 write:
eventBus.registerEvent('fakeEvent',handler)
function handler(data){
alert(data);
}
//In controller 2 write:
eventBus.fireEvent('fakeEvent','fakeData');
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-12-24 03:56:54
Oto szybki i brudny sposób.
// Add $injector as a parameter for your controller
function myAngularController($scope,$injector){
$scope.sendorders = function(){
// now you can use $injector to get the
// handle of $rootScope and broadcast to all
$injector.get('$rootScope').$broadcast('sinkallships');
};
}
Oto przykładowa funkcja dodana do dowolnego kontrolera rodzeństwa:
$scope.$on('sinkallships', function() {
alert('Sink that ship!');
});
I oczywiście tutaj jest Twój HTML:
<button ngclick="sendorders()">Sink Enemy Ships</button>
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
2013-07-30 19:51:35
Starting angular 1.5 and it ' s component based development focus. Zalecanym sposobem interakcji komponentów jest użycie właściwości "require" i powiązanie właściwości (wejście/wyjście).
Komponent wymagałby innego komponentu (na przykład komponentu głównego) i otrzymałby odniesienie do jego kontrolera:
angular.module('app').component('book', {
bindings: {},
require: {api: '^app'},
template: 'Product page of the book: ES6 - The Essentials',
controller: controller
});
Możesz następnie użyć metod komponentu głównego w komponencie potomnym:
$ctrl.api.addWatchedBook('ES6 - The Essentials');
To jest główny kontroler komponentu Funkcja:
function addWatchedBook(bookName){
booksWatched.push(bookName);
}
Oto kompletny przegląd architektury: Komunikacja komponentowa
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
2017-06-11 17:01:46
Możesz uzyskać dostęp do tej funkcji hello w dowolnym miejscu w module
Controller one
$scope.save = function() {
$scope.hello();
}
Drugi kontroler
$rootScope.hello = function() {
console.log('hello');
}
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-03-22 05:09:42
Stworzę usługę i użyję powiadomienia.
- Tworzenie metody w usłudze powiadomień
- Utwórz ogólną metodę nadawania powiadomień w usłudze powiadomień.
- z kontrolera źródłowego wywołaj notificationService.Metoda. W razie potrzeby przekazuję również odpowiedni obiekt do persist.
- w ramach metody zapisuję dane w usłudze powiadomień i wywołuję ogólną metodę powiadomień.
- w kontrolerze docelowym słucham ($scope.on) dla transmisja zdarzeń i danych dostępowych z usługi powiadamiania.
Ponieważ w każdym momencie Usługa powiadamiania jest singleton powinna być w stanie dostarczyć dane w całej
Hope this helps
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-08-29 04:45:50
Możesz użyć usługi wbudowanej AngularJS $rootScope
i wprowadzić tę usługę do obu kontrolerów.
Następnie można nasłuchiwać zdarzeń wywołanych w obiekcie $rootScope.
$rootScope dostarcza dwóch dyspozytorów zdarzeń o nazwie $emit and $broadcast
, które są odpowiedzialne za wysyłanie zdarzeń(mogą to być zdarzenia niestandardowe) i używają funkcji $rootScope.$on
do dodania detektora zdarzeń.
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-30 09:19:28
Powinieneś skorzystać z usługi, ponieważ $rootscope
jest dostęp z całej aplikacji i zwiększa obciążenie, lub używasz rootparams, jeśli Twoje dane nie są większe.
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-12-24 03:55:57
function mySrvc() {
var callback = function() {
}
return {
onSaveClick: function(fn) {
callback = fn;
},
fireSaveClick: function(data) {
callback(data);
}
}
}
function controllerA($scope, mySrvc) {
mySrvc.onSaveClick(function(data) {
console.log(data)
})
}
function controllerB($scope, mySrvc) {
mySrvc.fireSaveClick(data);
}
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-12-24 03:56:40
Można to zrobić za pomocą zdarzeń kątowych, które są $emit i $broadcast. Zgodnie z naszą wiedzą jest to najlepszy, skuteczny i skuteczny sposób.
Najpierw wywołujemy funkcję z jednego kontrolera.
var myApp = angular.module('sample', []);
myApp.controller('firstCtrl', function($scope) {
$scope.sum = function() {
$scope.$emit('sumTwoNumber', [1, 2]);
};
});
myApp.controller('secondCtrl', function($scope) {
$scope.$on('sumTwoNumber', function(e, data) {
var sum = 0;
for (var a = 0; a < data.length; a++) {
sum = sum + data[a];
}
console.log('event working', sum);
});
});
Możesz również użyć $rootScope zamiast $ scope. Użyj kontrolera odpowiednio.
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
2017-05-25 13:16:41