Lavorare con $ scope. $ Emit e $ scope. $ On


887

Come posso inviare il mio $scopeoggetto da un controller a un altro usando .$emite .$onmetodi?

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

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

Non funziona come penso che dovrebbe. Come funzionano $emite $onfunzionano?


6
Solo per i lettori futuri: non utilizzare $rootScopeper trasmettere / emettere quando può essere evitato.
Mistalis,

Risposte:


1499

Prima di tutto, la relazione dell'ambito genitore-figlio è importante. Hai due possibilità per emettere alcuni eventi:

  • $broadcast - invia l'evento verso il basso a tutti gli ambiti figlio,
  • $emit - invia l'evento verso l'alto attraverso la gerarchia dell'ambito.

Non so nulla della relazione dei controller (ambiti), ma ci sono diverse opzioni:

  1. Se scope of firstCtrlè genitore secondCtrldell'ambito, il codice dovrebbe funzionare sostituendo $emitcon $broadcastin firstCtrl:

    function firstCtrl($scope)
    {
        $scope.$broadcast('someEvent', [1,2,3]);
    }
    
    function secondCtrl($scope)
    {
        $scope.$on('someEvent', function(event, mass) { console.log(mass); });
    }
  2. Nel caso in cui non vi sia alcuna relazione genitore-figlio tra gli ambiti, è possibile iniettare $rootScopenel controller e trasmettere l'evento a tutti gli ambiti figlio (ovvero anche secondCtrl).

    function firstCtrl($rootScope)
    {
        $rootScope.$broadcast('someEvent', [1,2,3]);
    }
  3. Infine, quando è necessario inviare l'evento dal controller figlio agli ambiti verso l'alto, è possibile utilizzare $scope.$emit. Se l'ambito di firstCtrlè padre secondCtrldell'ambito:

    function firstCtrl($scope)
    {
        $scope.$on('someEvent', function(event, data) { console.log(data); });
    }
    
    function secondCtrl($scope)
    {
        $scope.$emit('someEvent', [1,2,3]);
    }

8
Esiste un modo per generare un evento da un servizio a un controller?
Zlatko,

29
Sì, teoricamente, potresti $rootScopeinserire il tuo servizio e trasmettere l'evento dal servizio.
Zbynour,

13
@Zlatko Sono abbastanza sicuro che i servizi per impostazione predefinita siano privi di ambito e hai bisogno di un ambito per partecipare al sistema degli eventi. Quindi in qualche modo devi fornire un ambito al tuo servizio. $ rootScope è la soluzione più generica a tale scopo, ma se si desidera che il servizio invii eventi da un ambito diverso, il controller potrebbe passare il proprio ambito al servizio impostando una proprietà sul servizio e ora il servizio può utilizzare il ambito del controllore. Una tecnica più semplice potrebbe essere che il controller fornisca una funzione al servizio che il servizio può chiamare direttamente.
Oran Dennison,

3
Se stai usando un iframe questo articolo ti sarà utile charemza.name/blog/posts/angularjs/iframe/…
leticia,

1
I servizi possono essere iniettati $rootScope, ma voglio sapere che se emetto un evento da un servizio (disattivato $rootScope), a cui continuerà a percorrere l'evento $rootScope; PERCHÉ, se $broadcastpercola GIÙ la gerarchia e $emitpercola SU - cosa succede TRA "SU" e "GIÙ" - poiché l'emittente / emittente è anche l'ascoltatore (?). E se volessi che l'evento fosse silente su TUTTI gli ambiti "UPWARD" e TUTTI "DOWNWARD", ma solo "udibile" allo stesso livello del dispatcher?
Cody,

145

Vorrei inoltre suggerire una quarta opzione come migliore alternativa alle opzioni proposte da @zbynour.

Utilizzare $rootScope.$emitpiuttosto che $rootScope.$broadcastindipendentemente dalla relazione tra il controller di trasmissione e di ricezione. In questo modo, l'evento rimane nel set di $rootScope.$$listenersconsiderando che con $rootScope.$broadcastl'evento si propaga a tutti gli ambiti secondari, la maggior parte dei quali probabilmente non saranno comunque ascoltatori di quell'evento. E, naturalmente, alla fine del controller di ricezione che usi $rootScope.$on.

Per questa opzione è necessario ricordare di distruggere i listener rootScope del controller:

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

3
Questo sarebbe quindi sostanzialmente un bus di evento centrale corretto?
jusopi,

5
In un certo senso sì, il vantaggio è che si evita la propagazione degli eventi.
Thalis K.,

3
@ThalisK. grazie per questa opzione. Evita la propagazione ma d'altra parte richiede l' $rootScopeiniezione nei controller (cosa non necessaria in generale). Ma sicuramente un'altra opzione, grazie!
zbynour,

77
Attenzione che $ rootScope vive per sempre. Se il controller viene eseguito due volte, qualsiasi $ rootScope. $ All'interno verrà eseguito due volte e gli eventi rilevati comporteranno una richiamata due volte. Se invece usi $ scope. $ On, il callback verrà distrutto insieme al tuo controller implicitamente da AngularJS.
Filip Sobczak,

1
Secondo il commento di @FilipSobczak, puoi evitare questo comportamento indesiderato sciogliendo il gestore dell'evento $ destroy con il seguente codice jsfiddle.net/ndqexjsg/1
Krzysztof Grzybek

111

Come posso inviare il mio oggetto $ scope da un controller a un altro usando i metodi. $ Emit e. $ On?

Puoi inviare qualsiasi oggetto desiderato all'interno della gerarchia della tua app, incluso $ scope .

Ecco una rapida idea di come funzionano la trasmissione e l' emissione .

Notare i nodi seguenti; tutti nidificati all'interno del nodo 3. Si utilizza broadcast ed emette quando si ha questo scenario.

Nota: il numero di ciascun nodo in questo esempio è arbitrario; potrebbe essere facilmente il numero uno; il numero due; o anche il numero 1.348. Ogni numero è solo un identificatore per questo esempio. Il punto di questo esempio è mostrare l'annidamento di controller / direttive angolari.

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

Dai un'occhiata a questo albero. Come rispondete alle seguenti domande?

Nota: Ci sono altri modi per rispondere a queste domande, ma qui parleremo di trasmissione e Emit . Inoltre, quando leggi il testo sotto, supponi che ogni numero abbia il suo file (direttiva, controller) ex one.js, two.js, three.js.

In che modo il nodo 1 parla al nodo 3 ?

Nel file one.js

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

Nel file three.js : il nodo più in alto di tutti i nodi figlio necessari per comunicare.

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

In che modo il nodo 2 parla al nodo 3?

Nel file two.js

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

Nel file three.js : il nodo più in alto di tutti i nodi figlio necessari per comunicare.

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

In che modo il nodo 3 parla al nodo 1 e / o al nodo 2?

Nel file three.js : il nodo più in alto di tutti i nodi figlio necessari per comunicare.

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

Nel file one.js && two.js qualunque file si desideri catturare il messaggio o entrambi.

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

In che modo il nodo 2 parla al nodo 1?

Nel file two.js

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

Nel file three.js : il nodo più in alto di tutti i nodi figlio necessari per comunicare.

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

Nel file one.js

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

PERÒ

Quando hai tutti questi nodi figlio nidificati che provano a comunicare in questo modo, vedrai rapidamente molti $ on , $ broadcast e $ emit .

Ecco cosa mi piace fare.

Nel NODO GENITORE superiore ( 3 in questo caso ...), che potrebbe essere il tuo controllore principale ...

Quindi, nel file three.js

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

Ora in qualsiasi nodo figlio devi solo $ emettere il messaggio o prenderlo usando $ on .

NOTA: Normalmente è abbastanza facile incrociare le conversazioni in un percorso nidificato senza usare $ emit , $ broadcast o $ on , il che significa che la maggior parte dei casi d'uso è quando si sta tentando di ottenere il nodo 1 per comunicare con il nodo 2 o viceversa.

In che modo il nodo 2 parla al nodo 1?

Nel file two.js

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

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

Nel file three.js : il nodo più in alto di tutti i nodi figlio necessari per comunicare.

Abbiamo già gestito questo ricordi?

Nel file one.js

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

Dovrai comunque usare $ su con ogni valore specifico che vuoi catturare, ma ora puoi creare quello che ti piace in uno dei nodi senza doversi preoccupare di come far passare il messaggio attraverso il gap del nodo genitore mentre catturiamo e trasmettiamo i pushChangesToAllNodes generici .

Spero che sia di aiuto...


come decidere quale è 3,2 e 1?
HIRA THAKUR,

3, 2 e 1 sono controller o direttive nidificati. Mentre crei la tua app, tieni a mente il tuo annidamento e applica la logica sopra. Per un esempio, potremmo dire che 3 è $ rootScope dell'applicazione; e tutto è annidato sotto di esso. 3, 2 e 1 sono arbitrari.
SoEzPz,

Grandi esempi! Ma sto ancora pensando che sia meglio usare il proprio event-dispatcher in parent per comunicare un gruppo di controller. Utile anche per mantenere la creazione del dispatcher come servizio per usarlo come modello.
DenisKolodin,

1
Secondo i documenti angolari su $ 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. quindi tu (come me) otterrai un ciclo infinito se implementi ctrl1 parlando con ctrl2 con $on('x', function(e, data) { $broadcast('x', data) })su ctrl3. Avrai bisogno di queste linee prima di trasmettere; if (e.targetScope.$id === $scope.$id) { return; }
Renato Gama,

39

Per inviare $scope objectda un controller a un altro, parlerò di $rootScope.$broadcaste $rootScope.$emitqui come sono più utilizzati.

Caso 1 :

. $ RootScope $ trasmesso: -

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

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

$rootScopel'ascoltatore non viene distrutto automaticamente. Devi distruggerlo usando $destroy. È meglio usarlo $scope.$onperché gli ascoltatori $scopevengono distrutti automaticamente, cioè non appena viene distrutto $ 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 differenza principale in $ emit e $ broadcast è che l'evento $ rootScope. $ Emit deve essere ascoltato usando $ rootScope. $ On, perché l'evento emesso non scende mai attraverso l'albero dell'ambito. .
Anche in questo caso devi distruggere l'ascoltatore come nel caso di $ broadcast.

Modificare:

Preferisco non usare $rootScope.$broadcast + $scope.$onma usare $rootScope.$emit+ $rootScope.$on. La $rootScope.$broadcast + $scope.$oncombinazione può causare seri problemi di prestazioni. Questo perché l'evento passerà attraverso tutti gli ambiti.

Modifica 2 :

Il problema risolto in questa risposta è stato risolto in angular.js versione 1.2.7. $ broadcast ora evita il gorgogliamento degli ambiti non registrati e funziona alla stessa velocità di $ emit.


10

È necessario utilizzare $ rootScope per inviare e acquisire eventi tra controller nella stessa app. Iniettare la dipendenza $ rootScope nei controller. Ecco un esempio funzionante.

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

Gli eventi collegati nell'oggetto $ scope funzionano solo nel controller proprietario. La comunicazione tra controller avviene tramite $ rootScope o Servizi.


7

È possibile chiamare un servizio dal controller che restituisce una promessa e quindi utilizzarlo nel controller. E utilizzare ulteriormente $emito $broadcastper informare altri controller al riguardo. Nel mio caso, ho dovuto effettuare chiamate http tramite il mio servizio, quindi ho fatto qualcosa del genere:

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 il mio servizio è simile a questo

    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

Questa è la mia funzione:

$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
Penso che questa sia una cattiva pratica poiché il tuo rootScope sarà ingombra. Vedere 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

Gli ambiti possono essere utilizzati per propagare, inviare eventi ai figli o ai genitori dell'ambito.

$ emit - propaga l'evento al genitore. $ broadcast : propaga l'evento ai bambini. $ on - metodo per ascoltare gli eventi, propagato da $ emit e $ broadcast.

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

esempio app.js :

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

Qui puoi testare il codice: http://jsfiddle.net/zp6v0rut/41/


2

Il codice seguente mostra i due controller secondari da cui gli eventi vengono inviati verso l'alto al controller principale (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

Secondo i documenti dell'evento angularjs, l'estremità ricevente dovrebbe contenere argomenti con una struttura simile

@params

- L'evento {Object} è l'oggetto dell'evento contenente informazioni sull'evento

- {Object} sostiene che sono passati dalla call (Nota che questo può essere solo uno dei migliori per inviare sempre un oggetto dizionario)

$scope.$on('fooEvent', function (event, args) { console.log(args) }); Dal tuo codice

Inoltre, se stai cercando di ottenere informazioni condivise da rendere disponibili su diversi controller, c'è un altro modo per ottenere questo e cioè i servizi angolari. Dal momento che i servizi sono singoli, le informazioni possono essere archiviate e recuperate attraverso i controller. Crea semplicemente getter e funzioni setter in quel servizio, esporre queste funzioni, creare variabili globali nel servizio e usarle per memorizzare le informazioni


0

La via più facile :

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>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.