$ on y $ broadcast en angular

282

Tengo un footerController y codeScannerController con diferentes vistas.

angular.module('myApp').controller('footerController', ["$scope", function($scope) {}]);

angular.module('myApp').controller('codeScannerController', ["$scope", function($scope) {
console.log("start");
$scope.startScanner = function(){...

Cuando hago clic en a <li>en footer.html, debería obtener este evento en codeScannerController.

<li class="button" ng-click="startScanner()">3</li>

Creo que se puede realizar con $ony $broadcast, pero no sé cómo y no puedo encontrar ejemplos en ningún lado.

Alice Polansky
fuente

Respuestas:

631

Si quieres $broadcastusar el $rootScope:

$scope.startScanner = function() {

    $rootScope.$broadcast('scanner-started');
}

Y luego para recibir, use el $scopede su controlador:

$scope.$on('scanner-started', function(event, args) {

    // do what you want to do
});

Si lo desea, puede pasar argumentos cuando $broadcast:

$rootScope.$broadcast('scanner-started', { any: {} });

Y luego recibirlos:

$scope.$on('scanner-started', function(event, args) {

    var anyThing = args.any;
    // do what you want to do
});

Documentación para esto dentro de los documentos de Scope .

Davin Tryon
fuente
2
Puedes nombrar el evento como quieras.
Davin Tryon
55
Asegúrese de que tiene $ alcance. $ Apply (); sus cambios!
Ismail
44
@Ismail ¿Por qué ... y dónde?
Jaans
77
¿Hay alguna práctica recomendada para almacenar estas cadenas en algún lugar en lugar de codificar el mensaje de difusión?
rperryng
8
@Ismail $scope.$apply()solo es necesario cuando se cambia el modelo fuera del marco angular (como en un setTimeout, una devolución de llamada de diálogo o una devolución de llamada ajax), en otras palabras, $apply()ya se activa después de que todo el código .$on()haya finalizado.
th3uiguy
97

En primer lugar, una breve descripción de $on(), $broadcast()y$emit() :

  • .$on(name, listener) - Escucha un evento específico por un determinado name
  • .$broadcast(name, args)- Transmita un evento a través $scopede todos los niños
  • .$emit(name, args)- Emitir un evento en la $scopejerarquía a todos los padres, incluido el$rootScope

Basado en el siguiente HTML (vea el ejemplo completo aquí ):

<div ng-controller="Controller1">
    <button ng-click="broadcast()">Broadcast 1</button>
    <button ng-click="emit()">Emit 1</button>
</div>

<div ng-controller="Controller2">
    <button ng-click="broadcast()">Broadcast 2</button>
    <button ng-click="emit()">Emit 2</button>
    <div ng-controller="Controller3">
        <button ng-click="broadcast()">Broadcast 3</button>
        <button ng-click="emit()">Emit 3</button>
        <br>
        <button ng-click="broadcastRoot()">Broadcast Root</button>
        <button ng-click="emitRoot()">Emit Root</button>
    </div>
</div>

Los eventos disparados atravesarán lo $scopessiguiente:

  • Difusión 1: solo la verá el controlador 1 $scope
  • Emitir 1 - $scopeEntonces será visto por el Controlador 1$rootScope
  • Broadcast 2: será visto por el controlador 2 y $scopeluego por el controlador 3$scope
  • Emitir 2 - $scopeEntonces será visto por el Controlador 2$rootScope
  • Difusión 3: solo la verá el controlador 3 $scope
  • Emitir 3: será visto por el controlador 3 $scope, el controlador 2 y $scopeluego$rootScope
  • Broadcast Raíz - será visto por $rootScopey $scopede todos los controladores (1, 2 y luego 3)
  • Emitir raíz: solo será visto por $rootScope

JavaScript para desencadenar eventos (de nuevo, puede ver un ejemplo de trabajo aquí ):

app.controller('Controller1', ['$scope', '$rootScope', function($scope, $rootScope){
    $scope.broadcastAndEmit = function(){
        // This will be seen by Controller 1 $scope and all children $scopes 
        $scope.$broadcast('eventX', {data: '$scope.broadcast'});

        // Because this event is fired as an emit (goes up) on the $rootScope,
        // only the $rootScope will see it
        $rootScope.$emit('eventX', {data: '$rootScope.emit'});
    };
    $scope.emit = function(){
        // Controller 1 $scope, and all parent $scopes (including $rootScope) 
        // will see this event
        $scope.$emit('eventX', {data: '$scope.emit'});
    };

    $scope.$on('eventX', function(ev, args){
        console.log('eventX found on Controller1 $scope');
    });
    $rootScope.$on('eventX', function(ev, args){
        console.log('eventX found on $rootScope');
    });
}]);
th3uiguy
fuente
¿Cómo puedo imaginar la jerarquía de mi aplicación con el ejemplo que has dado? ¿Cómo puede un controlador ser padre o hijo? Lo que estoy tratando de decir es que tengo una serie de estados, por ejemplo. LoginCtrl -> homeCrl -> notifyCtrl y así sucesivamente.
HIRA THAKUR
26

Una cosa que debes saber es que el prefijo $ se refiere a un Método Angular, los prefijos $$ se refieren a métodos angulares que debes evitar.

A continuación se muestra una plantilla de ejemplo y sus controladores, exploraremos cómo $ broadcast / $ on puede ayudarnos a lograr lo que queremos.

<div ng-controller="FirstCtrl">
    <input ng-model="name"/> 
    <button ng-click="register()">Register </button>
</div>

<div ng-controller="SecondCtrl">
    Registered Name: <input ng-model="name"/> 
</div>

Los controladores son

app.controller('FirstCtrl', function($scope){
    $scope.register = function(){

    }
});

app.controller('SecondCtrl', function($scope){

});

Mi pregunta para usted es ¿cómo pasa el nombre al segundo controlador cuando un usuario hace clic en registrarse? Puede encontrar múltiples soluciones, pero la que vamos a usar es usar $ broadcast y $ on.

$ broadcast vs $ emit

¿Cuál deberíamos usar? $ broadcast canalizará a todos los elementos dom de los niños y $ emit canalizará la dirección opuesta a todos los elementos dom ancestrales.

La mejor manera de evitar decidir entre $ emit o $ broadcast es canalizar desde $ rootScope y usar $ broadcast para todos sus hijos. Lo que hace que nuestro caso sea mucho más fácil ya que nuestros elementos dom son hermanos.

Agregar $ rootScope y permite $ broadcast

app.controller('FirstCtrl', function($rootScope, $scope){
    $scope.register = function(){
        $rootScope.$broadcast('BOOM!', $scope.name)
    }
});

Tenga en cuenta que agregamos $ rootScope y ahora estamos usando $ broadcast (broadcastName, argumentos). Para broadcastName, queremos darle un nombre único para que podamos capturar ese nombre en nuestro secondCtrl. ¡He elegido BOOM! solo por diversión. Los segundos argumentos 'argumentos' nos permiten pasar valores a los oyentes.

Recibiendo nuestra transmisión

En nuestro segundo controlador, necesitamos configurar el código para escuchar nuestra transmisión

app.controller('SecondCtrl', function($scope){
  $scope.$on('BOOM!', function(events, args){
    console.log(args);
    $scope.name = args; //now we've registered!
  })
});

Es realmente así de simple. Ejemplo en vivo

Otras formas de lograr resultados similares

Intente evitar el uso de este conjunto de métodos, ya que no es ni eficiente ni fácil de mantener, pero es una forma sencilla de solucionar los problemas que pueda tener.

Por lo general, puede hacer lo mismo utilizando un servicio o simplificando sus controladores. No discutiremos esto en detalle, pero pensé en mencionarlo por completo.

Por último, tenga en cuenta que una transmisión realmente útil para escuchar es '$ destroy' nuevamente, puede ver que $ significa que es un método u objeto creado por los códigos de proveedor. De todos modos, $ destroy se transmite cuando se destruye un controlador, es posible que desee escuchar esto para saber cuándo se retira su controlador.

Yang Li
fuente
2
Como advertencia, intente no utilizar demasiadas transmisiones / emisiones en su aplicación. Pueden ser extremadamente difíciles de administrar, especialmente en una aplicación grande, ya que rastrear las raíces de estos eventos es una tarea muy difícil.
Yang Li
1
//Your broadcast in service

(function () { 
    angular.module('appModule').factory('AppService', function ($rootScope, $timeout) {

    function refreshData() {  
        $timeout(function() {         
            $rootScope.$broadcast('refreshData');
        }, 0, true);      
    }

    return {           
        RefreshData: refreshData
    };
}); }());

//Controller Implementation
 (function () {
    angular.module('appModule').controller('AppController', function ($rootScope, $scope, $timeout, AppService) {            

       //Removes Listeners before adding them 
       //This line will solve the problem for multiple broadcast call                             
       $scope.$$listeners['refreshData'] = [];

       $scope.$on('refreshData', function() {                                                    
          $scope.showData();             
       });

       $scope.onSaveDataComplete = function() { 
         AppService.RefreshData();
       };
    }); }());
Arenoso
fuente