Trabajando con $ scope. $ Emit y $ scope. $ On

887

¿Cómo puedo enviar mi $scopeobjeto de un controlador a otro usando .$emity .$onmétodos?

function firstCtrl($scope) {
    $scope.$emit('someEvent', [1,2,3]);
}

function secondCtrl($scope) {
    $scope.$on('someEvent', function(mass) { console.log(mass); });
}

No funciona como creo que debería. ¿Cómo hacer $emity $ontrabajar?

Paul Kononenko
fuente
66
Solo para futuros lectores: no lo use $rootScopepara transmitir / emitir cuando se pueda evitar.
Mistalis

Respuestas:

1499

En primer lugar, la relación de alcance padre-hijo sí importa. Tienes dos posibilidades para emitir algún evento:

  • $broadcast - envía el evento hacia abajo a todos los ámbitos secundarios,
  • $emit - despacha el evento hacia arriba a través de la jerarquía de alcance.

No sé nada sobre la relación de sus controladores (ámbitos), pero hay varias opciones:

  1. Si el alcance de firstCtrles padre del secondCtrlalcance, su código debería funcionar reemplazando $emitpor $broadcasten firstCtrl:

    function firstCtrl($scope)
    {
        $scope.$broadcast('someEvent', [1,2,3]);
    }
    
    function secondCtrl($scope)
    {
        $scope.$on('someEvent', function(event, mass) { console.log(mass); });
    }
  2. En caso de que no haya una relación padre-hijo entre sus ámbitos, puede inyectar $rootScopeen el controlador y transmitir el evento a todos los ámbitos secundarios (es decir, también secondCtrl).

    function firstCtrl($rootScope)
    {
        $rootScope.$broadcast('someEvent', [1,2,3]);
    }
  3. Finalmente, cuando necesite enviar el evento desde el controlador secundario a ámbitos hacia arriba, puede usarlo $scope.$emit. Si el alcance de firstCtrles padre del secondCtrlalcance:

    function firstCtrl($scope)
    {
        $scope.$on('someEvent', function(event, data) { console.log(data); });
    }
    
    function secondCtrl($scope)
    {
        $scope.$emit('someEvent', [1,2,3]);
    }
zbynour
fuente
8
¿Hay alguna manera de disparar un evento desde un servicio a un controlador?
Zlatko
29
Sí, teóricamente, podría inyectar $rootScopeen su servicio y transmitir el evento desde el servicio.
zbynour
13
@Zlatko Estoy bastante seguro de que los servicios por defecto no tienen alcance, y necesitas un alcance para participar en el sistema de eventos. Por lo tanto, de alguna manera debe proporcionar un alcance a su servicio. $ rootScope es la solución de propósito más general para eso, pero si desea que su servicio envíe eventos desde un alcance diferente, su controlador podría pasar su alcance al servicio estableciendo una propiedad en el servicio, y ahora el servicio puede usar el alcance del controlador. Una técnica más directa podría ser que el controlador proporcione una función al servicio que el servicio puede llamar directamente.
Oran Dennison
3
Si está utilizando un iframe, este artículo será útil charemza.name/blog/posts/angularjs/iframe/…
leticia
1
Los servicios pueden inyectarse $rootScope, pero quiero saber que si emito un evento desde un servicio (fuera de $rootScope), el evento aún se filtrará $rootScope; PORQUE, si se $broadcastfiltra hacia ABAJO la jerarquía, y se $emitfiltra hacia ARRIBA, ¿qué sucede ENTRE "ARRIBA" y "ABAJO"? Ya que el emisor / emisor también es el oyente (?). ¿Qué sucede si deseo que el evento sea silencioso para TODOS los ámbitos "HACIA ARRIBA" y TODOS "HACIA ABAJO", pero solo sea "audible" en el mismo nivel que el despachador?
Cody
145

Además, sugeriría una cuarta opción como una mejor alternativa a las opciones propuestas por @zbynour.

Use en $rootScope.$emitlugar de $rootScope.$broadcastindependientemente de la relación entre la transmisión y el controlador de recepción. De esa manera, el evento permanece dentro del conjunto de $rootScope.$$listenersmientras que con $rootScope.$broadcastel evento se propaga a todos los ámbitos secundarios, la mayoría de los cuales probablemente no serán oyentes de ese evento de todos modos. Y, por supuesto, al final del controlador receptor solo se usa $rootScope.$on.

Para esta opción, debe recordar destruir los oyentes rootScope del controlador:

var unbindEventHandler = $rootScope.$on('myEvent', myHandler);
$scope.$on('$destroy', function () {
  unbindEventHandler();
});
Thalis K.
fuente
3
Esto básicamente serviría como un bus de eventos central ¿correcto?
jusopi
55
En cierto sentido, sí, el beneficio es que evita la propagación de eventos.
Thalis K.
3
@ThalisK. Gracias por esta opción. Evita la propagación pero, por otro lado, requiere $rootScopeinyección en los controladores (lo que no se necesita en general). Pero seguramente otra opción, gracias!
zbynour
77
Tenga en cuenta que $ rootScope vive para siempre. Si su controlador se ejecuta dos veces, cualquier $ rootScope. $ En su interior se ejecutará dos veces, y los eventos capturados provocarán una devolución de llamada invocada dos veces. Si utiliza $ scope. $ On en su lugar, AngularJS destruirá la devolución de llamada junto con su controlador implícitamente.
Filip Sobczak
1
Según el comentario de @FilipSobczak, puede evitar este comportamiento no deseado al desvincular el controlador en el evento $ destroy con el siguiente código jsfiddle.net/ndqexjsg/1
Krzysztof Grzybek
111

¿Cómo puedo enviar mi objeto $ scope de un controlador a otro usando los métodos. $ Emit y. $ On?

Puede enviar cualquier objeto que desee dentro de la jerarquía de su aplicación, incluido $ scope .

He aquí una idea rápida de cómo difusión y emiten trabajo.

Observe los nodos a continuación; todos anidados dentro del nodo 3. Usas broadcast y emites cuando tienes este escenario.

Nota: El número de cada nodo en este ejemplo es arbitrario; fácilmente podría ser el número uno; el numero dos; o incluso el número 1,348. Cada número es solo un identificador para este ejemplo. El objetivo de este ejemplo es mostrar el anidamiento de controladores / directivas angulares.

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

Mira este árbol ¿Cómo responde las siguientes preguntas?

Nota: Hay otras maneras de responder a estas preguntas, pero aquí vamos a discutir emisión y emiten . Además, cuando lea el texto a continuación, suponga que cada número tiene su propio archivo (directiva, controlador) ex one.js, two.js, three.js.

¿Cómo habla el nodo 1 al nodo 3 ?

En el archivo one.js

scope.$emit('messageOne', someValue(s));

En el archivo three.js : el nodo superior para todos los nodos secundarios necesarios para comunicarse.

scope.$on('messageOne', someValue(s));

¿Cómo habla el nodo 2 al nodo 3?

En el archivo two.js

scope.$emit('messageTwo', someValue(s));

En el archivo three.js : el nodo superior para todos los nodos secundarios necesarios para comunicarse.

scope.$on('messageTwo', someValue(s));

¿Cómo habla el nodo 3 al nodo 1 y / o al nodo 2?

En el archivo three.js : el nodo superior para todos los nodos secundarios necesarios para comunicarse.

scope.$broadcast('messageThree', someValue(s));

En el archivo one.js && two.js, el archivo que desee capturar el mensaje o ambos.

scope.$on('messageThree', someValue(s));

¿Cómo habla el nodo 2 al nodo 1?

En el archivo two.js

scope.$emit('messageTwo', someValue(s));

En el archivo three.js : el nodo superior para todos los nodos secundarios necesarios para comunicarse.

scope.$on('messageTwo', function( event, data ){
  scope.$broadcast( 'messageTwo', data );
});

En el archivo one.js

scope.$on('messageTwo', someValue(s));

SIN EMBARGO

Cuando tenga todos estos nodos hijos anidados que intentan comunicarse de esta manera, verá rápidamente muchos $ on , $ broadcast's y $ emit's .

Esto es lo que me gusta hacer.

En el NODO DE PADRES superior ( 3 en este caso ...), que puede ser su controlador principal ...

Entonces, en el archivo three.js

scope.$on('pushChangesToAllNodes', function( event, message ){
  scope.$broadcast( message.name, message.data );
});

Ahora en cualquiera de los nodos secundarios solo necesita $ emitir el mensaje o atraparlo usando $ on .

NOTA: Normalmente es bastante fácil cruzar la conversación en una ruta anidada sin usar $ emit , $ broadcast o $ on , lo que significa que la mayoría de los casos de uso son cuando intenta que el nodo 1 se comunique con el nodo 2 o viceversa.

¿Cómo habla el nodo 2 al nodo 1?

En el archivo two.js

scope.$emit('pushChangesToAllNodes', sendNewChanges());

function sendNewChanges(){ // for some event.
  return { name: 'talkToOne', data: [1,2,3] };
}

En el archivo three.js : el nodo superior para todos los nodos secundarios necesarios para comunicarse.

Ya manejamos este, ¿recuerdas?

En el archivo one.js

scope.$on('talkToOne', function( event, arrayOfNumbers ){
  arrayOfNumbers.forEach(function(number){
    console.log(number);
  });
});

Aún necesitará usar $ on con cada valor específico que desee capturar, pero ahora puede crear lo que quiera en cualquiera de los nodos sin tener que preocuparse por cómo transmitir el mensaje a través del espacio del nodo principal a medida que capturamos y transmitimos el genérico pushChangesToAllNodes .

Espero que esto ayude...

SoEzPz
fuente
¿Cómo decidir cuál es 3,2 y 1?
HIRA THAKUR
Los 3, 2 y 1 son controladores o directivas anidadas. Al crear su aplicación, tenga en cuenta su anidamiento y aplique la lógica anterior. Por ejemplo, podríamos decir que 3 es el $ rootScope de la aplicación; y todo está anidado debajo de él. 3, 2 y 1 son arbitrarios.
SoEzPz
¡Grandes ejemplos! Pero sigo pensando que es mejor usar el propio despachador de eventos en los padres para comunicar un grupo de controladores. También es útil para mantener la creación del despachador como servicio para usarlo como patrón.
DenisKolodin
1
De acuerdo con los documentos angulares en $ 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. por lo tanto, usted (como yo) obtendrá un bucle infinito si implementa ctrl1 hablando con ctrl2 con $on('x', function(e, data) { $broadcast('x', data) })ctrl3. Necesitará estas líneas antes de transmitir; if (e.targetScope.$id === $scope.$id) { return; }
Renato Gama
39

Para enviar $scope objectde un controlador a otro, hablaré sobre $rootScope.$broadcasty $rootScope.$emitaquí, ya que se usan más.

Caso 1 :

$ rootScope. $ broadcast: -

$rootScope.$broadcast('myEvent',$scope.data);//Here `myEvent` is event name

$rootScope.$on('myEvent', function(event, data) {} //listener on `myEvent` event

$rootScopeEl oyente no se destruye automáticamente. Necesitas destruirlo usando $destroy. Es mejor usarlo $scope.$onya que los oyentes $scopese destruyen automáticamente, es decir, tan pronto como se destruye $ scope.

$scope.$on('myEvent', function(event, data) {}

O,

  var customeEventListener = $rootScope.$on('myEvent', function(event, data) {

  }
  $scope.$on('$destroy', function() {
        customeEventListener();
  });

Caso 2:

$ rootScope. $ emit:

   $rootScope.$emit('myEvent',$scope.data);

   $rootScope.$on('myEvent', function(event, data) {}//$scope.$on not works

La principal diferencia en $ emit y $ broadcast es que el evento $ rootScope. $ Emit debe escucharse usando $ rootScope. $ On, porque el evento emitido nunca desciende por el árbol de alcance. .
En este caso también debes destruir al oyente como en el caso de $ broadcast.

Editar:

Prefiero no usar $rootScope.$broadcast + $scope.$onsino usar $rootScope.$emit+ $rootScope.$on. El $rootScope.$broadcast + $scope.$oncombo puede causar serios problemas de rendimiento. Esto se debe a que el evento burbujeará en todos los ámbitos.

Edición 2 :

El problema abordado en esta respuesta se ha resuelto en angular.js versión 1.2.7. $ broadcast ahora evita burbujear sobre ámbitos no registrados y se ejecuta tan rápido como $ emit.

Ved
fuente
10

Debe usar $ rootScope para enviar y capturar eventos entre controladores en la misma aplicación. Inyecte la dependencia de $ rootScope a sus controladores. Aquí hay un ejemplo de trabajo.

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); });
        }
}

Los eventos vinculados al objeto $ scope solo funcionan en el controlador propietario. La comunicación entre los controladores se realiza a través de $ rootScope o Services.

kyasar
fuente
7

Puede llamar a un servicio desde su controlador que le devuelve una promesa y luego usarlo en su controlador. Y más uso $emito $broadcastpara informar a otros controladores al respecto. En mi caso, tuve que hacer llamadas http a través de mi servicio, así que hice algo como esto:

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
        })
    }

y mi servicio se ve así

    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);
                });

        }

    }])
ribhu
fuente
4

Esta es mi función:

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

$rootScope.$on('setTitle', function(event, title) {
    if (scope.item) 
        scope.item.name = title;
    else 
        scope.item = {name: title};
});
trai bui
fuente
1
Creo que esta es una mala práctica ya que su rootScope estará desordenado. Ver stackoverflow.com/questions/24830679/…
SKuijers el
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>
Prashant_M
fuente
2

El (los) alcance (s) se pueden usar para propagar, enviar eventos al ámbito secundario o primario.

$ emit : propaga el evento a padre. $ broadcast : propaga el evento a los niños. $ en : método para escuchar los eventos, propagado por $ emit y $ broadcast.

ejemplo 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>

ejemplo app.js :

angular.module('appExample', [])
.controller('EventCtrl', ['$scope', function($scope) {
  $scope.count = 0;
  $scope.$on('MyEvent', function() {
    $scope.count++;
  });
}]);

Aquí puede probar el código: http://jsfiddle.net/zp6v0rut/41/

Vasyl Gutnyk
fuente
2

El siguiente código muestra los dos subcontroladores desde donde los eventos se envían hacia arriba al controlador principal (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/

Shushanth Pallegar
fuente
0

De acuerdo con los documentos del evento angularjs, el extremo receptor debería contener argumentos con una estructura como

@params

- El evento {Object} es el objeto del evento que contiene información sobre el evento

- Argumentos {Objeto} que pasa la persona que llama (Tenga en cuenta que esto solo puede ser uno mejor para enviar siempre un objeto de diccionario)

$scope.$on('fooEvent', function (event, args) { console.log(args) }); De su código

Además, si está tratando de obtener una información compartida para que esté disponible en diferentes controladores, hay otra forma de lograrlo y son los servicios angulares. Dado que los servicios son únicos, la información se puede almacenar y buscar en los controladores. Simplemente cree getter y establece funciones en ese servicio, expone estas funciones, crea variables globales en el servicio y las usa para almacenar la información

Wajih Siddiqui
fuente
0

La forma más fácil :

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>
Sangwin Gawande
fuente