Работа с $ scope. $ Emit и $ scope. $ On


887

Как я могу отправить свой $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 работает и работает?


6
Только для будущих читателей: не используйте $rootScopeдля трансляции / эмиссии, когда этого можно избежать.
Мисталис

Ответы:


1499

Прежде всего, имеет значение отношение области родитель-потомок. У вас есть две возможности для создания какого-либо события:

  • $broadcast - отправляет событие вниз во все дочерние области,
  • $emit - отправляет событие вверх через иерархию области действия.

Я ничего не знаю о вашем отношении контроллеров (областей), но есть несколько вариантов:

  1. Если сфера 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); });
    }
  2. Если между вашими областями нет родительско-дочерних отношений, вы можете ввести $rootScopeих в контроллер и передать событие всем дочерним областям (т. Е. Также secondCtrl).

    function firstCtrl($rootScope)
    {
        $rootScope.$broadcast('someEvent', [1,2,3]);
    }
  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]);
    }

8
Есть ли способ запустить событие от службы к контроллеру?
Златко

29
Да, теоретически вы могли бы внедрить $rootScopeв ваш сервис и транслировать событие из сервиса.
zbynour

13
@ Златко Я уверен, что сервисы по умолчанию не входят в сферу, и вам нужна сфера для участия в системе событий. Так что вам как-то нужно предоставить сферу для вашего сервиса. $ rootScope является наиболее универсальным решением для этого, но если вы хотите, чтобы ваша служба отправляла события из другой области, ваш контроллер мог бы передать свою область службе, задав свойство службы, и теперь служба может использовать сфера действия контроллера Более простой метод может быть для контроллера, чтобы предоставить сервису функцию, которую сервис может вызвать напрямую.
Оран Деннисон

3
Если вы используете iframe, эта статья будет полезна charemza.name/blog/posts/angularjs/iframe/…
leticia

1
Службы могут внедрять $rootScope- но я хочу знать, что если я отправлю событие из службы (отключено $rootScope), то событие все равно будет предоставлено $rootScope; ПОТОМУ ЧТО, если $broadcastпросачивается ВНИЗ по иерархии и $emitпросачивается вверх - что происходит между «ВВЕРХ» и «ВНИЗ» - поскольку вещатель / излучатель также является слушателем (?). Что если я хочу, чтобы событие было бесшумным для ВСЕХ областей «Вверх» и «Вниз», но было бы «слышимым» только на том же уровне, что и диспетчер?
Коди

145

Я бы дополнительно предложил 4-й вариант в качестве лучшей альтернативы предложенным @zbynour вариантам.

Используйте, $rootScope.$emitа не $rootScope.$broadcastнезависимо от отношений между передающим и принимающим контроллером. Таким образом, событие остается в наборе, $rootScope.$$listenersтогда $rootScope.$broadcastкак событие распространяется на все дочерние области, большинство из которых, вероятно, в любом случае не будут слушателями этого события. И, конечно, в конце принимающего контроллера вы просто используете$rootScope.$on .

Для этой опции вы должны не забыть уничтожить слушатели rootScope контроллера:

var unbindEventHandler = $rootScope.$on('myEvent', myHandler);
$scope.$on('$destroy', function () {
  unbindEventHandler();
});

3
Тогда это будет в основном служить центральной шиной событий правильно?
Юзопи

5
В некотором смысле, да, вы избегаете распространения событий.
Талис К.

3
@ThalisK. спасибо за этот вариант. Это позволяет избежать распространения, но, с другой стороны, требует $rootScopeвнедрения в контроллеры (что вообще не требуется). Но, конечно, еще один вариант, спасибо!
zbynour

78
Остерегайтесь, что $ rootScope живет вечно. Если ваш контроллер запускается дважды, любой $ rootScope. $ Внутри него будет запущен дважды, а перехваченные события приведут к обратному вызову, вызываемому дважды. Если вместо этого вы используете $ scope. $ On, обратный вызов будет неявно уничтожен вместе с вашим контроллером AngularJS.
Филип Собчак

1
Согласно комментарию @FilipSobczak, вы можете избежать этого нежелательного поведения, отсоединив обработчик события $ destroy со следующим кодом jsfiddle.net/ndqexjsg/1
Krzysztof Grzybek

111

Как я могу отправить свой объект $ scope с одного контроллера на другой, используя методы. $ Emit и. $ On?

Вы можете отправить любой объект в иерархии вашего приложения, включая $ scope .

Вот краткое представление о том, как транслировать и излучать работают .

Обратите внимание на узлы ниже; все они вложены в узел 3. Вы используете широковещательную рассылку и передачу при наличии этого сценария.

Примечание: номер каждого узла в этом примере является произвольным; это может легко быть номер один; номер два; или даже число 1348. Каждый номер является просто идентификатором для этого примера. Смысл этого примера - показать вложение угловых контроллеров / директив.

                 3
           ------------
           |          |
         -----     ------
         1   |     2    |
      ---   ---   ---  ---
      | |   | |   | |  | |

Посмотри на это дерево. Как вы отвечаете на следующие вопросы?

Примечание: Есть другие способы , чтобы ответить на эти вопросы, но здесь мы будем обсуждать трансляцию и Emit . Кроме того, при чтении текста ниже предположим, что у каждого числа есть собственный файл (директива, контроллер), например, 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 .

Надеюсь это поможет...


как решить какой из 3,2 и 1?
ХИРА ТАКУР,

3, 2 и 1 являются либо вложенными контроллерами, либо директивами. Создавая свое приложение, имейте в виду свое вложение и применяйте приведенную выше логику. Например, мы можем сказать, что 3 - это $ rootScope приложения; и все вложено ниже этого. 3, 2 и 1 произвольны.
SoEzPz

Прекрасные примеры! Но я все еще думаю, что лучше использовать собственный обработчик событий в родительском для связи группы контроллеров. Также полезно сохранить создание диспетчера как сервис, чтобы использовать его как шаблон.
ДенисКолодин

1
Согласно угловым документам на $ broadcast, The event life cycle starts at the scope on which $broadcast was called. All listeners listening for name event on this scope get notified. вы (как и я) получите бесконечный цикл, если вы реализуете ctrl1, общаясь с ctrl2 с помощью $on('x', function(e, data) { $broadcast('x', data) })ctrl3. Вам понадобятся эти линии перед трансляцией; if (e.targetScope.$id === $scope.$id) { return; }
Ренато Гама

39

Для отправки $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.


10

Вы должны использовать $ 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.


7

Вы можете вызвать сервис из вашего контроллера, который возвращает обещание, а затем использовать его в своем контроллере. И далее использовать $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);
                });

        }

    }])

4

Это моя функция:

$rootScope.$emit('setTitle', newVal.full_name);

$rootScope.$on('setTitle', function(event, title) {
    if (scope.item) 
        scope.item.name = title;
    else 
        scope.item = {name: title};
});

1
Я думаю, что это плохая практика, так как ваш rootScope будет загроможден. См stackoverflow.com/questions/24830679/...
SKuijers

4
<!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>

2

Область (ы) могут быть использованы для распространения, отправки события в область потомков или родителей.

$ 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/


2

Код ниже показывает два субконтроллера, откуда события отправляются вверх на родительский контроллер (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);   
    }
}

http://jsfiddle.net/shushanthp/zp6v0rut/


0

Согласно документации событий angularjs, принимающая сторона должна содержать аргументы со структурой, подобной

@params

- Событие {Object} является объектом события, содержащим информацию о событии

- Аргументы {Object}, которые передаются вызываемым пользователем (обратите внимание, что это может быть только один, поэтому лучше всегда отправлять в объект словаря)

$scope.$on('fooEvent', function (event, args) { console.log(args) }); Из вашего кода

Кроме того, если вы пытаетесь получить доступ к общему фрагменту информации на разных контроллерах, есть еще один способ добиться этого, и это угловые сервисы. Поскольку сервисы являются единичными, информация может храниться и выбираться через контроллеры. Просто создайте геттер и установочные функции в этом сервисе, предоставляют эти функции, создают глобальные переменные в сервисе и используют их для хранения информации


0

Самый простой способ:

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>
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.