Работа с $scope.$ Emit и $scope.$ On
Как я могу отправить свой $scope
объект от одного контроллера к другому с помощью .$emit
а также .$on
методы?
function firstCtrl($scope) {
$scope.$emit('someEvent', [1,2,3]);
}
function secondCtrl($scope) {
$scope.$on('someEvent', function(mass) { console.log(mass); });
}
Это не работает так, как я думаю. Как $emit
а также $on
Работа?
13 ответов
Прежде всего, имеет значение отношение области родитель-потомок. У вас есть две возможности для создания какого-либо события:
$broadcast
- отправляет событие вниз во все дочерние области,$emit
- отправляет событие вверх через иерархию области действия.
Я ничего не знаю о вашем отношении контроллеров (областей), но есть несколько вариантов:
Если сфера
firstCtrl
является родителемsecondCtrl
область, ваш код должен работать, заменив$emit
от$broadcast
вfirstCtrl
:function firstCtrl($scope) { $scope.$broadcast('someEvent', [1,2,3]); } function secondCtrl($scope) { $scope.$on('someEvent', function(event, mass) { console.log(mass); }); }
Если между вашими областями нет отношения родитель-ребенок, вы можете ввести
$rootScope
в контроллер и транслировать событие на все дочерние области (т.е. такжеsecondCtrl
).function firstCtrl($rootScope) { $rootScope.$broadcast('someEvent', [1,2,3]); }
Наконец, когда вам нужно отправить событие из дочернего контроллера в области вверх, вы можете использовать
$scope.$emit
, Если сфераfirstCtrl
является родителемsecondCtrl
объем:function firstCtrl($scope) { $scope.$on('someEvent', function(event, data) { console.log(data); }); } function secondCtrl($scope) { $scope.$emit('someEvent', [1,2,3]); }
Я бы дополнительно предложил 4-й вариант в качестве лучшей альтернативы предложенным @zbynour вариантам.
использование $rootScope.$emit
скорее, чем $rootScope.$broadcast
независимо от отношения между передающим и принимающим контроллером. Таким образом, событие остается в пределах набора $rootScope.$$listeners
тогда как с $rootScope.$broadcast
событие распространяется на все дочерние области, большинство из которых, вероятно, в любом случае не будут слушателями этого события. И, конечно, в конце принимающего контроллера вы просто используете $rootScope.$on
,
Для этой опции вы должны не забыть уничтожить слушатели rootScope контроллера:
var unbindEventHandler = $rootScope.$on('myEvent', myHandler);
$scope.$on('$destroy', function () {
unbindEventHandler();
});
Как я могу отправить свой объект $scope с одного контроллера на другой, используя методы. $ Emit и. $ On?
Вы можете отправить любой объект в иерархии вашего приложения, включая $scope.
Вот краткое представление о том, как работают трансляция и передача.
Обратите внимание на узлы ниже; все они вложены в узел 3. Вы используете широковещательную рассылку и передачу при наличии этого сценария
Примечание: номер каждого узла в этом примере является произвольным; это может легко быть номер один; номер два; или даже число 1348. Каждый номер является просто идентификатором для этого примера. Смысл этого примера - показать вложение угловых контроллеров / директив.
3
------------
| |
----- ------
1 | 2 |
--- --- --- ---
| | | | | | | |
Посмотри на это дерево. Как вы отвечаете на следующие вопросы?
Примечание: есть и другие способы ответить на эти вопросы, но здесь мы обсудим трансляцию и передачу. Кроме того, при чтении текста ниже предполагается, что у каждого числа есть свой собственный файл (директива, контроллер), например, one.js, two.js, three.js.
Как узел 1 говорит с узлом 3?
В файле one.js
scope.$emit('messageOne', someValue(s));
В файле three.js - самый верхний узел для всех дочерних узлов, необходимых для связи.
scope.$on('messageOne', someValue(s));
Как узел 2 говорит с узлом 3?
В файле two.js
scope.$emit('messageTwo', someValue(s));
В файле three.js - самый верхний узел для всех дочерних узлов, необходимых для связи.
scope.$on('messageTwo', someValue(s));
Как узел 3 общается с узлом 1 и / или узлом 2?
В файле three.js - самый верхний узел для всех дочерних узлов, необходимых для связи.
scope.$broadcast('messageThree', someValue(s));
В файле one.js && two.js, в зависимости от того, какой файл вы хотите перехватить, или оба.
scope.$on('messageThree', someValue(s));
Как узел 2 говорит с узлом 1?
В файле two.js
scope.$emit('messageTwo', someValue(s));
В файле three.js - самый верхний узел для всех дочерних узлов, необходимых для связи.
scope.$on('messageTwo', function( event, data ){
scope.$broadcast( 'messageTwo', data );
});
В файле one.js
scope.$on('messageTwo', someValue(s));
ТЕМ НЕ МЕНИЕ
Когда у вас есть все эти вложенные дочерние узлы, пытающиеся взаимодействовать таким образом, вы быстро увидите много $ on, $ broadcast и $ emit.
Вот что мне нравится делать.
В самом верхнем РОДИТЕЛЬСКОМ УЗЛЕ (3 в данном случае...), который может быть вашим родительским контроллером...
Итак, в файле three.js
scope.$on('pushChangesToAllNodes', function( event, message ){
scope.$broadcast( message.name, message.data );
});
Теперь в любом из дочерних узлов вам нужно всего лишь отправить сообщение или перехватить его, используя $ on.
ПРИМЕЧАНИЕ. Обычно довольно просто пересечь разговор по одному вложенному пути без использования $ emit, $ broadcast или $ on, что означает, что в большинстве случаев используется, когда вы пытаетесь заставить узел 1 взаимодействовать с узлом 2 или наоборот.
Как узел 2 говорит с узлом 1?
В файле two.js
scope.$emit('pushChangesToAllNodes', sendNewChanges());
function sendNewChanges(){ // for some event.
return { name: 'talkToOne', data: [1,2,3] };
}
В файле three.js - самый верхний узел для всех дочерних узлов, необходимых для связи.
Мы уже справились с этим, помните?
В файле one.js
scope.$on('talkToOne', function( event, arrayOfNumbers ){
arrayOfNumbers.forEach(function(number){
console.log(number);
});
});
Вам все еще нужно будет использовать $ on с каждым конкретным значением, которое вы хотите перехватить, но теперь вы можете создавать все, что вам нравится, в любом из узлов, не беспокоясь о том, как передать сообщение через пробел родительского узла, когда мы ловим и транслируем универсальные pushChangesToAllNodes.
Надеюсь это поможет...
Отправлять $scope object
от одного контроллера к другому я буду обсуждать $rootScope.$broadcast
а также $rootScope.$emit
здесь как они используются больше всего.
Случай 1:
$ RootScope $ широковещательный:. -
$rootScope.$broadcast('myEvent',$scope.data);//Here `myEvent` is event name
$rootScope.$on('myEvent', function(event, data) {} //listener on `myEvent` event
$rootScope
слушатель не уничтожается автоматически. Вы должны уничтожить его, используя $destroy
, Лучше использовать $scope.$on
как слушатели на $scope
уничтожаются автоматически, т.е. как только $ scope будет уничтожен.
$scope.$on('myEvent', function(event, data) {}
Или же,
var customeEventListener = $rootScope.$on('myEvent', function(event, data) {
}
$scope.$on('$destroy', function() {
customeEventListener();
});
Случай 2:
. $ RootScope $ выделяют:
$rootScope.$emit('myEvent',$scope.data);
$rootScope.$on('myEvent', function(event, data) {}//$scope.$on not works
Основное различие в $ emit и $ broadcast заключается в том, что событие $ rootScope. $ Emit должно прослушиваться с использованием $ rootScope. $ On, поскольку событие emitted никогда не проходит через дерево областей действия.,
В этом случае вы также должны уничтожить слушателя, как в случае $ broadcast.
Редактировать:
Я предпочитаю не использовать
$rootScope.$broadcast + $scope.$on
но использовать$rootScope.$emit+ $rootScope.$on
,$rootScope.$broadcast + $scope.$on
комбо может вызвать серьезные проблемы с производительностью. Это потому, что событие будет проходить через все области видимости.
Изменить 2:
Проблема, затронутая в этом ответе, была решена в angular.js версии 1.2.7. $broadcast теперь избегает пузырей над незарегистрированными областями и работает так же быстро, как $ emit.
Вы должны использовать $ rootScope для отправки и захвата событий между контроллерами в одном приложении. Внедрите зависимость $ rootScope в свои контроллеры. Вот рабочий пример.
app.controller('firstCtrl', function($scope, $rootScope) {
function firstCtrl($scope) {
{
$rootScope.$emit('someEvent', [1,2,3]);
}
}
app.controller('secondCtrl', function($scope, $rootScope) {
function secondCtrl($scope)
{
$rootScope.$on('someEvent', function(event, data) { console.log(data); });
}
}
События, связанные с объектом $ scope, просто работают в контроллере владельца. Связь между контроллерами осуществляется через $ rootScope или Services.
Вы можете вызвать сервис из вашего контроллера, который возвращает обещание, а затем использовать его в своем контроллере. И дальнейшее использование $emit
или же $broadcast
проинформировать об этом других контролеров. В моем случае мне пришлось делать http-вызовы через мой сервис, поэтому я сделал что-то вроде этого:
function ParentController($scope, testService) {
testService.getList()
.then(function(data) {
$scope.list = testService.list;
})
.finally(function() {
$scope.$emit('listFetched');
})
function ChildController($scope, testService) {
$scope.$on('listFetched', function(event, data) {
// use the data accordingly
})
}
и мой сервис выглядит так
app.service('testService', ['$http', function($http) {
this.list = [];
this.getList = function() {
return $http.get(someUrl)
.then(function(response) {
if (typeof response.data === 'object') {
list = response.data.results;
return response.data;
} else {
// invalid response
return $q.reject(response.data);
}
}, function(response) {
// something went wrong
return $q.reject(response.data);
});
}
}])
<!DOCTYPE html>
<html>
<head>
<script src= "http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script>
var app = angular.module('MyApp',[]);
app.controller('parentCtrl',function($scope){
$scope.$on('MyEvent',function(event,data){
$scope.myData = data;
});
});
app.controller('childCtrl',function($scope){
$scope.fireEvent = function(){
$scope.$emit('MyEvent','Any Data');
}
});
</script>
</head>
<body ng-app="MyApp">
<div ng-controller="parentCtrl" ng-model="myName">
{{myData}}
<div ng-controller="childCtrl">
<button ng-click="fireEvent()">Fire Event</button>
</div>
</div>
</body>
</html>
Это моя функция:
$rootScope.$emit('setTitle', newVal.full_name);
$rootScope.$on('setTitle', function(event, title) {
if (scope.item)
scope.item.name = title;
else
scope.item = {name: title};
});
Я закончил тем, что добавил внешнюю библиотеку EventEmitter в проект как сервис и внедрил ее везде, где мне нужно. Таким образом, я могу "излучать" и "включать" что угодно в любом месте, не заботясь о наследовании области действия. Это меньше проблем и, конечно, лучшая производительность. Также более читабельным для меня.
Поддержка подстановочных знаков: EventEmitter2
Хорошая производительность: eventemitter3
Другая альтернатива: капельное
Код ниже показывает два субконтроллера, откуда события отправляются вверх на родительский контроллер (rootScope)
<body ng-app="App">
<div ng-controller="parentCtrl">
<p>City : {{city}} </p>
<p> Address : {{address}} </p>
<div ng-controller="subCtrlOne">
<input type="text" ng-model="city" />
<button ng-click="getCity(city)">City !!!</button>
</div>
<div ng-controller="subCtrlTwo">
<input type="text" ng-model="address" />
<button ng-click="getAddrress(address)">Address !!!</button>
</div>
</div>
</body>
var App = angular.module('App', []);
// parent controller
App.controller('parentCtrl', parentCtrl);
parentCtrl.$inject = ["$scope"];
function parentCtrl($scope) {
$scope.$on('cityBoom', function(events, data) {
$scope.city = data;
});
$scope.$on('addrBoom', function(events, data) {
$scope.address = data;
});
}
// sub controller one
App.controller('subCtrlOne', subCtrlOne);
subCtrlOne.$inject = ['$scope'];
function subCtrlOne($scope) {
$scope.getCity = function(city) {
$scope.$emit('cityBoom', city);
}
}
// sub controller two
App.controller('subCtrlTwo', subCtrlTwo);
subCtrlTwo.$inject = ["$scope"];
function subCtrlTwo($scope) {
$scope.getAddrress = function(addr) {
$scope.$emit('addrBoom', addr);
}
}
Область (и) могут использоваться для распространения, отправки события в область потомков или родительский элемент.
$ emit - распространяет событие на родителя. $ broadcast - распространяет событие среди детей.$ on - метод для прослушивания событий, распространяемых с помощью $ emit и $ broadcast.
пример index.html:
<div ng-app="appExample" ng-controller="EventCtrl">
Root(Parent) scope count: {{count}}
<div>
<button ng-click="$emit('MyEvent')">$emit('MyEvent')</button>
<button ng-click="$broadcast('MyEvent')">$broadcast('MyEvent')</button><br>
Childrent scope count: {{count}}
</div>
</div>
пример app.js:
angular.module('appExample', [])
.controller('EventCtrl', ['$scope', function($scope) {
$scope.count = 0;
$scope.$on('MyEvent', function() {
$scope.count++;
});
}]);
Здесь вы можете проверить код: http://jsfiddle.net/zp6v0rut/41/
Самый простой способ:
HTML
<div ng-app="myApp" ng-controller="myCtrl">
<button ng-click="sendData();"> Send Data </button>
</div>
JavaScript
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $rootScope) {
function sendData($scope) {
var arrayData = ['sam','rumona','cubby'];
$rootScope.$emit('someEvent', arrayData);
}
});
app.controller('yourCtrl', function($scope, $rootScope) {
$rootScope.$on('someEvent', function(event, data) {
console.log(data);
});
});
</script>
Согласно документации событий angularjs, принимающая сторона должна содержать аргументы со структурой, подобной
@params
- Событие {Object} является объектом события, содержащим информацию о событии
- Аргументы {Object}, которые передаются вызываемым пользователем (обратите внимание, что это может быть только один, поэтому лучше всегда отправлять в объект словаря)
$scope.$on('fooEvent', function (event, args) { console.log(args) });
Из вашего кода
Кроме того, если вы пытаетесь получить доступ к общему объему информации на разных контроллерах, есть еще один способ добиться этого, и это угловые сервисы. Так как сервисы являются единичными, информация может храниться и выбираться через контроллеры. Просто создайте геттер и установочные функции в этом сервисе, предоставляют эти функции, создают глобальные переменные в сервисе и используют их для хранения информации