Trabalhando com $ scope. $ Emit e $ scope. $ On

887

Como posso enviar meu $scopeobjeto de um controlador para outro usando .$emite .$onmétodos?

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

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

Não funciona da maneira que eu acho que deveria. Como $emite $ontrabalhar?

Paul Kononenko
fonte
6
Apenas para futuros leitores: não use $rootScopepara transmissão / emissão quando puder ser evitado.
Mistalis

Respostas:

1499

Antes de tudo, a relação de escopo pai-filho é importante. Você tem duas possibilidades para emitir algum evento:

  • $broadcast - envia o evento para baixo para todos os escopos filho,
  • $emit - despacha o evento para cima através da hierarquia de escopo.

Não sei nada sobre a relação dos seus controladores (escopos), mas existem várias opções:

  1. Se âmbito da firstCtrlé pai do secondCtrlescopo, seu código deve funcionar, substituindo $emitpor $broadcastem firstCtrl:

    function firstCtrl($scope)
    {
        $scope.$broadcast('someEvent', [1,2,3]);
    }
    
    function secondCtrl($scope)
    {
        $scope.$on('someEvent', function(event, mass) { console.log(mass); });
    }
    
  2. Caso não exista relação pai-filho entre seus escopos, você pode injetar $rootScopeno controlador e transmitir o evento para todos os escopos filhos (ou seja, também secondCtrl).

    function firstCtrl($rootScope)
    {
        $rootScope.$broadcast('someEvent', [1,2,3]);
    }
    
  3. Finalmente, quando você precisar despachar o evento do controlador filho para os escopos para cima, poderá usar $scope.$emit. Se o escopo de firstCtrlfor pai do secondCtrlescopo:

    function firstCtrl($scope)
    {
        $scope.$on('someEvent', function(event, data) { console.log(data); });
    }
    
    function secondCtrl($scope)
    {
        $scope.$emit('someEvent', [1,2,3]);
    }
    
zbynour
fonte
8
Existe uma maneira de disparar um evento de um serviço para um controlador?
Zlatko
29
Sim, teoricamente, você pode injetar $rootScopeem seu serviço e transmitir o evento a partir do serviço.
precisa saber é o seguinte
13
@Zlatko Tenho certeza de que os serviços por padrão são sem escopo e você precisa de um escopo para participar do sistema de eventos. Então, de alguma forma, você precisa fornecer um escopo ao seu serviço. $ rootScope é a solução de uso geral, mas se você quiser que seu serviço envie eventos de um escopo diferente, seu controlador poderá passar seu escopo para o serviço definindo uma propriedade no serviço e agora o serviço pode usar o escopo do controlador. Uma técnica mais direta pode ser o controlador fornecer uma função ao serviço que o serviço pode chamar diretamente.
Oran Dennison
3
Se você estiver usando um iframe, este artigo será útil charemza.name/blog/posts/angularjs/iframe/…
leticia
1
Os serviços podem injetar $rootScope- mas eu quero saber que, se eu emitir um evento de um serviço (desativado $rootScope), o evento ainda passará para $rootScope; PORQUE, se $broadcastpercorrer a hierarquia para baixo e $emitpercorrer PARA CIMA - o que acontece ENTRE "PARA CIMA" e "PARA BAIXO" -, pois o emissor / emissor também é o ouvinte (?). E se eu quiser que o evento seja silencioso para TODOS os escopos "PARA CIMA" e "TODO PARA BAIXO", mas seja apenas "audível" no mesmo nível do expedidor?
Cody
145

Além disso, eu sugeriria uma quarta opção como uma alternativa melhor às opções propostas pelo @zbynour.

Use em $rootScope.$emitvez de $rootScope.$broadcastindependentemente da relação entre a transmissão e o controlador de recebimento. Dessa forma, o evento permanece dentro do conjunto $rootScope.$$listenersenquanto que $rootScope.$broadcasto evento se propaga a todos os escopos filhos, a maioria dos quais provavelmente não serão ouvintes desse evento. E, claro, no final do controlador receptor, você apenas usa $rootScope.$on.

Para esta opção, lembre-se de destruir os ouvintes rootScope do controlador:

var unbindEventHandler = $rootScope.$on('myEvent', myHandler);
$scope.$on('$destroy', function () {
  unbindEventHandler();
});
Thalis K.
fonte
3
Isso basicamente serviria como um barramento central de eventos correto?
Jusopi
5
Em certo sentido, sim, o benefício é que você evita a propagação de eventos.
Thalis K.
3
@ThalisK. obrigado por esta opção. Evita a propagação, mas, por outro lado, requer $rootScopeinjeção nos controladores (o que geralmente não é necessário). Mas certamente outra opção, thx!
Zbynour
77
Cuidado que $ rootScope vive para sempre. Se seu controlador for executado duas vezes, qualquer $ rootScope. $ Dentro dele será executado duas vezes e os eventos capturados resultarão em um retorno de chamada chamado duas vezes. Se você usar $ scope. $ On, o retorno de chamada será destruído juntamente com o seu controlador implicitamente pelo AngularJS.
Filip Sobczak 28/11
1
De acordo com o comentário @FilipSobczak, você pode evitar esse comportamento indesejado pelo manipulador unbinding em $ destruir evento com o seguinte código jsfiddle.net/ndqexjsg/1
Krzysztof Grzybek
111

Como posso enviar meu objeto $ scope de um controlador para outro usando os métodos. $ Emit e. $ Nos métodos?

Você pode enviar qualquer objeto que desejar dentro da hierarquia do seu aplicativo, incluindo $ scope .

Aqui está uma idéia rápida sobre como a transmissão e a emissão funcionam.

Observe os nós abaixo; tudo aninhado no nó 3. Você usa broadcast e emite quando possui esse cenário.

Nota: O número de cada nó neste exemplo é arbitrário; poderia facilmente ser o número um; o número dois; ou até o número 1.348. Cada número é apenas um identificador para este exemplo. O objetivo deste exemplo é mostrar o aninhamento de controladores / diretivas angulares.

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

Confira esta árvore. Como você responde às seguintes perguntas?

Nota: Existem outras maneiras de responder a essas perguntas, mas aqui discutiremos transmissão e emissão . Além disso, ao ler o texto abaixo, assuma que cada número tem seu próprio arquivo (diretiva, controlador) ex one.js, two.js, three.js.

Como o nó 1 fala com o nó 3 ?

No arquivo one.js

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

No arquivo three.js - o nó superior de todos os nós filhos necessários para se comunicar.

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

Como o nó 2 fala com o nó 3?

No arquivo two.js

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

No arquivo three.js - o nó superior de todos os nós filhos necessários para se comunicar.

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

Como o nó 3 fala com o nó 1 e / ou o nó 2?

No arquivo three.js - o nó superior de todos os nós filhos necessários para se comunicar.

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

No arquivo one.js && two.js, qualquer arquivo que você deseja capturar a mensagem ou ambos.

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

Como o nó 2 fala com o nó 1?

No arquivo two.js

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

No arquivo three.js - o nó superior de todos os nós filhos necessários para se comunicar.

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

No arquivo one.js

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

CONTUDO

Quando você tem todos esses nós filhos aninhados tentando se comunicar assim, rapidamente verá muitos $ on , $ broadcast e $ emit .

Aqui está o que eu gosto de fazer.

No NÚMERO DE PAI mais alto ( 3 neste caso ...), que pode ser seu controlador pai ...

Então, no arquivo three.js

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

Agora, em qualquer um dos nós filhos, você só precisa emitir a mensagem $ ou capturá-la usando $ on .

NOTA: Normalmente, é bastante fácil cruzar a conversa em um caminho aninhado sem usar $ emit , $ broadcast ou $ on , o que significa que a maioria dos casos de uso é para quando você está tentando fazer o nó 1 se comunicar com o nó 2 ou vice-versa.

Como o nó 2 fala com o nó 1?

No arquivo two.js

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

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

No arquivo three.js - o nó superior de todos os nós filhos necessários para se comunicar.

Já lidamos com este, lembra?

No arquivo one.js

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

Você ainda precisará usar $ on com cada valor específico que deseja capturar, mas agora pode criar o que quiser em qualquer um dos nós sem ter que se preocupar em como transmitir a mensagem através da lacuna do nó pai à medida que capturamos e transmitimos o pushChangesToAllNodes genérico .

Espero que isto ajude...

SoEzPz
fonte
como decidir qual é 3,2 e 1?
HIRA THAKUR
Os 3, 2 e 1 são controladores ou diretivas aninhadas. Ao criar seu aplicativo, lembre-se de aninhar e aplique a lógica acima. Por exemplo, poderíamos dizer que 3 é o $ rootScope do aplicativo; e tudo está aninhado abaixo dela. 3, 2 e 1 são arbitrários.
SoEzPz
Grandes exemplos! Mas ainda estou pensando que é melhor usar o próprio despachante de eventos no pai para comunicar o grupo de controladores. Também é útil para manter a criação do expedidor como serviço e usá-lo como padrão.
DenisKolodin
1
De acordo com documentos angulares em $ 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. portanto, você (como eu) obterá um loop infinito se implementar ctrl1 falando com ctrl2 com $on('x', function(e, data) { $broadcast('x', data) })em ctrl3. Você precisará dessas linhas antes de transmitir; if (e.targetScope.$id === $scope.$id) { return; }
Renato Gama
39

Para enviar $scope objectde um controlador para outro, discutirei sobre $rootScope.$broadcaste $rootScope.$emitaqui como eles são mais utilizados.

Caso 1 :

$ rootScope. $ broadcast: -

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

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

$rootScopeouvinte não são destruídos automaticamente. Você precisa destruí-lo usando $destroy. É melhor usar $scope.$oncomo os ouvintes $scopesão destruídos automaticamente, ou seja, assim que $ scope for destruído.

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

Ou,

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

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

Caso 2:

$ rootScope. $ emitem:

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

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

A principal diferença em $ emit e $ broadcast é que o evento $ rootScope. $ Emit deve ser escutado usando $ rootScope. $ On, porque o evento emitido nunca desce pela árvore do escopo. .
Nesse caso, você também deve destruir o ouvinte, como no caso de $ broadcast.

Editar:

Eu prefiro não usar, $rootScope.$broadcast + $scope.$onmas usar $rootScope.$emit+ $rootScope.$on. A $rootScope.$broadcast + $scope.$oncombinação pode causar sérios problemas de desempenho. Isso ocorre porque o evento passará por todos os escopos.

Edição 2 :

O problema abordado nesta resposta foi resolvido na versão 1.2.7 do angular.js. A transmissão $ agora evita borbulhar em escopos não registrados e roda tão rápido quanto $ emite.

Ved
fonte
10

Você deve usar $ rootScope para enviar e capturar eventos entre controladores no mesmo aplicativo. Injete a dependência de $ rootScope nos seus controladores. Aqui está um exemplo de trabalho.

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

Eventos vinculados ao objeto $ scope apenas funcionam no controlador do proprietário. A comunicação entre controladores é feita via $ rootScope ou Services.

kyasar
fonte
7

Você pode chamar um serviço do seu controlador que retorne uma promessa e usá-lo no seu controlador. E use ainda mais $emitou $broadcastpara informar outros controladores sobre isso. No meu caso, eu tive que fazer chamadas http através do meu serviço, então fiz algo assim:

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

e meu serviço fica assim

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

Esta é a minha função:

$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
fonte
1
Eu acho que isso é uma prática ruim, pois o seu rootScope ficará confuso. Veja 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>
Prashant_M
fonte
2

O (s) escopo (s) pode ser usado para propagar, despachar evento para o escopo filho ou pai.

$ emit - propaga o evento para o pai. $ broadcast - propaga o evento para crianças. $ on - método para escutar os eventos, propagados por $ emit e $ broadcast.

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

exemplo app.js :

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

Aqui você pode testar o código: http://jsfiddle.net/zp6v0rut/41/

Vasyl Gutnyk
fonte
2

O código abaixo mostra os dois subcontroladores de onde os eventos são despachados para o controlador pai (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
fonte
0

De acordo com a documentação do evento angularjs, o terminal receptor deve conter argumentos com uma estrutura como

@params

- Evento {Object} sendo o objeto de evento que contém informações sobre o evento

- {Objetos} args que são passados ​​pelo receptor (observe que esse pode ser apenas um melhor para enviar sempre um objeto de dicionário)

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

Além disso, se você estiver tentando obter uma informação compartilhada disponível em diferentes controladores, existe uma outra maneira de conseguir isso e que são serviços angulares.Como os serviços são singletons, as informações podem ser armazenadas e buscadas nos controladores. O setter funciona nesse serviço, expõe essas funções, cria variáveis ​​globais no serviço e as utiliza para armazenar as informações

Wajih Siddiqui
fonte
0

O jeito mais 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
fonte