Praca z $ scope. $ Emit i $ scope. $ On

887

Jak mogę wysłać mój $scopeobiekt z jednego kontrolera do drugiego za pomocą .$emiti .$onmetod?

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

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

Nie działa tak, jak myślę, że powinno. Jak działa $emiti $ondziała?

Paul Kononenko
źródło
6
Tylko dla przyszłych czytelników: nie używaj $rootScopedo nadawania / emitowania, gdy można tego uniknąć.
Mistalis

Odpowiedzi:

1499

Przede wszystkim relacja zakresu rodzic-dziecko ma znaczenie. Masz dwie możliwości wyemitowania jakiegoś zdarzenia:

  • $broadcast - wysyła zdarzenie w dół do wszystkich zakresów potomnych,
  • $emit - wywołuje zdarzenie w górę poprzez hierarchię zasięgu.

Nic nie wiem na temat relacji kontrolerów (zakresów), ale istnieje kilka opcji:

  1. Jeśli zakres firstCtrljest dominującym w secondCtrlzakresie, Twój kod powinien działać przez zastąpienie $emitprzez $broadcastw firstCtrl:

    function firstCtrl($scope)
    {
        $scope.$broadcast('someEvent', [1,2,3]);
    }
    
    function secondCtrl($scope)
    {
        $scope.$on('someEvent', function(event, mass) { console.log(mass); });
    }
    
  2. W przypadku braku relacji rodzic-dziecko między twoimi zakresami, możesz wstrzyknąć $rootScopedo kontrolera i transmitować zdarzenie do wszystkich zakresów potomnych (tj. Również secondCtrl).

    function firstCtrl($rootScope)
    {
        $rootScope.$broadcast('someEvent', [1,2,3]);
    }
    
  3. Wreszcie, gdy trzeba wysłać zdarzenie z kontrolera podrzędnego do zakresów w górę, możesz użyć $scope.$emit. Jeśli zakres firstCtrljest rodzicem secondCtrlzakresu:

    function firstCtrl($scope)
    {
        $scope.$on('someEvent', function(event, data) { console.log(data); });
    }
    
    function secondCtrl($scope)
    {
        $scope.$emit('someEvent', [1,2,3]);
    }
    
zbynour
źródło
8
Czy istnieje sposób na wywołanie zdarzenia z usługi do kontrolera?
Zlatko
29
Tak, teoretycznie możesz wstrzykiwać $rootScopedo swojej usługi i transmitować wydarzenie z usługi.
zbynour
13
@Zlatko Jestem pewien, że usługi domyślnie nie obejmują zakresu i potrzebujesz zakresu, aby wziąć udział w systemie wydarzeń. Więc w jakiś sposób musisz podać zakres swojej usługi. $ rootScope jest najbardziej uniwersalnym rozwiązaniem tego problemu, ale jeśli chcesz, aby Twoja usługa wysyłała zdarzenia z innego zakresu, kontroler może przekazać swój zakres do usługi, ustawiając właściwość usługi, a teraz usługa może korzystać z zakres kontrolera. Bardziej bezpośrednią techniką może być zapewnienie przez kontroler funkcji dla usługi, którą usługa może wywołać bezpośrednio.
Oran Dennison
3
Jeśli używasz iframe, ten artykuł będzie pomocny charemza.name/blog/posts/angularjs/iframe/…
leticia
1
Usługi mogą wstrzykiwać $rootScope- ale chcę wiedzieć, że jeśli wyemituję zdarzenie z usługi (poza $rootScope), to zdarzenie będzie nadal przenikać $rootScope; PONIEWAŻ, jeśli $broadcastprzenika W DÓŁ hierarchii i $emitprzenosi W GÓRĘ - co dzieje się MIĘDZY „UP” i „DOWN” - ponieważ nadawca / emiter jest również odbiornikiem (?). Co jeśli chcę, aby wydarzenie było ciche dla WSZYSTKICH zakresów „W GÓRĘ” i WSZYSTKICH „W DÓŁ”, ale było „słyszalne” tylko na tym samym poziomie co dyspozytor?
Cody
145

Dodatkowo zasugerowałbym czwartą opcję jako lepszą alternatywę dla proponowanych opcji @zbynour.

Użyj $rootScope.$emitraczej niż $rootScope.$broadcastniezależnie od relacji między nadawaniem a kontrolerem odbierającym. W ten sposób wydarzenie pozostaje w obrębie zbioru, $rootScope.$$listenerspodczas $rootScope.$broadcastgdy wydarzenie rozprzestrzenia się na wszystkie zakresy dzieci, z których większość i tak prawdopodobnie nie będzie słuchaczami tego wydarzenia. I oczywiście na końcu kontrolera odbierającego po prostu używasz $rootScope.$on.

W przypadku tej opcji należy pamiętać o zniszczeniu nasłuchiwania rootScope kontrolera:

var unbindEventHandler = $rootScope.$on('myEvent', myHandler);
$scope.$on('$destroy', function () {
  unbindEventHandler();
});
Thalis K.
źródło
3
Czy to w zasadzie służyłoby jako centralny autobus zdarzeń, prawda?
jusopi
5
W pewnym sensie tak, zaletą jest to, że unikasz propagacji zdarzeń.
Thalis K.
3
@ThalisK. dzięki za tę opcję. Unika rozprzestrzeniania się, ale z drugiej strony wymaga $rootScopewstrzyknięcia do kontrolerów (co na ogół nie jest potrzebne). Ale na pewno inna opcja, dzięki!
zbynour,
77
Uważaj, że $ rootScope żyje wiecznie. Jeśli kontroler jest uruchamiany dwukrotnie, dowolny $ rootScope. $ Wewnątrz będzie uruchamiany dwukrotnie, a wychwycone zdarzenia spowodują wywołanie zwrotne dwukrotnie. Jeśli zamiast tego użyjesz $ scope. $ On, wywołanie zwrotne zostanie zniszczone wraz z twoim kontrolerem niejawnie przez AngularJS.
Filip Sobczak
1
Zgodnie z komentarzem @FilipSobczak, można uniknąć tego niechcianego zachowania, odznaczając moduł obsługi zdarzenia $ destroy za pomocą następującego kodu jsfiddle.net/ndqexjsg/1
Krzysztof Grzybek
111

Jak mogę wysłać mój obiekt $ scope z jednego kontrolera do drugiego za pomocą metod. $ Emit i. $ Na metodach?

Możesz wysłać dowolny obiekt w hierarchii swojej aplikacji, w tym $ scope .

Oto krótki pomysł na temat sposobu nadawania i emitowania .

Zwróć uwagę na poniższe węzły; wszystkie zagnieżdżone w węźle 3. Korzystasz z rozgłaszania i emisji, gdy masz taki scenariusz.

Uwaga: Liczba każdego węzła w tym przykładzie jest dowolna; z łatwością może być numerem jeden; numer dwa; lub nawet liczba 1348. Każda liczba jest tylko identyfikatorem dla tego przykładu. Celem tego przykładu jest pokazanie zagnieżdżenia kontrolerów / dyrektyw Angular.

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

Sprawdź to drzewo. Jak odpowiesz na następujące pytania?

Uwaga: istnieją inne sposoby odpowiedzi na te pytania, ale tutaj omówimy transmisję i emisję . Ponadto, czytając poniższy tekst, załóż, że każdy numer ma własny plik (dyrektywę, kontroler) np. One.js, two.js, three.js.

Jak węzeł 1 przemawia do węzła 3 ?

W pliku one.js

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

W pliku three.js - najwyższy węzeł do wszystkich węzłów potomnych potrzebnych do komunikacji.

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

Jak węzeł 2 mówi do węzła 3?

W pliku two.js

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

W pliku three.js - najwyższy węzeł do wszystkich węzłów potomnych potrzebnych do komunikacji.

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

Jak węzeł 3 przemawia do węzła 1 i / lub węzła 2?

W pliku three.js - najwyższy węzeł do wszystkich węzłów potomnych potrzebnych do komunikacji.

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

W pliku one.js && two.js, w zależności od tego, który plik chcesz przechwycić, lub jedno i drugie.

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

Jak węzeł 2 przemawia do węzła 1?

W pliku two.js

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

W pliku three.js - najwyższy węzeł do wszystkich węzłów potomnych potrzebnych do komunikacji.

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

W pliku one.js

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

JEDNAK

Kiedy wszystkie te zagnieżdżone węzły podrzędne będą próbowały komunikować się w ten sposób, szybko zobaczysz wiele $ on , $ broadcast i $ emit .

Oto co lubię robić.

W górnej węzeł nadrzędny ( 3 w tym przypadku ...), którym może być kontroler rodzic ...

Tak więc w pliku three.js

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

Teraz w dowolnym węźle podrzędnym wystarczy tylko $ wyemitować wiadomość lub złapać ją za pomocą $ on .

UWAGA: Zwykle dość łatwo jest rozmawiać krzyżowo w jednej zagnieżdżonej ścieżce bez użycia $ emit , $ broadcast lub $ on , co oznacza, że ​​większość przypadków użycia występuje, gdy próbujesz zmusić węzeł 1 do komunikacji z węzłem 2 lub odwrotnie.

Jak węzeł 2 przemawia do węzła 1?

W pliku two.js

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

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

W pliku three.js - najwyższy węzeł do wszystkich węzłów potomnych potrzebnych do komunikacji.

Już sobie z tym poradziliśmy, pamiętasz?

W pliku one.js

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

Nadal będziesz musiał użyć $ on dla każdej konkretnej wartości, którą chcesz złapać, ale teraz możesz tworzyć, co chcesz w dowolnym z węzłów, nie martwiąc się o to, jak przekazać wiadomość przez lukę węzła nadrzędnego, gdy łapiemy i transmitujemy ogólne pushChangesToAllNodes .

Mam nadzieję że to pomoże...

SoEzPz
źródło
jak zdecydować, który to 3,2 i 1?
HIRA THAKUR
3, 2 i 1 są zagnieżdżonymi kontrolerami lub dyrektywami. Tworząc aplikację, pamiętaj o zagnieżdżaniu i zastosuj powyższą logikę. Na przykład możemy powiedzieć, że 3 jest $ rootScope aplikacji; i wszystko jest zagnieżdżone poniżej. 3, 2 i 1 są arbitralne.
SoEzPz
Świetne przykłady! Ale nadal myślę, że lepiej użyć własnego programu do wysyłania zdarzeń w obiekcie nadrzędnym, aby komunikować się z grupą kontrolerów. Przydaje się również, aby zachować tworzenie dyspozytora jako usługę, aby użyć go jako wzorca.
DenisKolodin,
1
Zgodnie z angularnymi dokumentami na $ 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. więc (podobnie jak ja) dostaniesz nieskończoną pętlę, jeśli zaimplementujesz ctrl1 rozmawiając z ctrl2 za pomocą $on('x', function(e, data) { $broadcast('x', data) })na ctrl3. Będziesz potrzebował tych linii przed emisją; if (e.targetScope.$id === $scope.$id) { return; }
Renato Gama,
39

Aby wysłać $scope objectz jednego kontrolera do drugiego, omówię $rootScope.$broadcasti $rootScope.$emittutaj, ponieważ są one najczęściej używane.

Przypadek 1 :

$ rootScope. $ broadcast: -

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

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

$rootScopesłuchacz nie jest automatycznie niszczony. Musisz go zniszczyć za pomocą $destroy. Lepiej jest używać, $scope.$ongdy nasłuchiwacze $scopesą niszczone automatycznie, tj. Jak tylko zniszczony jest zakres $.

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

Lub,

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

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

Przypadek 2:

$ rootScope. $ emit:

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

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

Główną różnicą w $ emit i $ broadcast jest to, że zdarzenie $ rootScope. $ Emit musi być nasłuchiwane za pomocą $ rootScope. $ On, ponieważ emitowane zdarzenie nigdy nie przechodzi przez drzewo zasięgu. .
W tym przypadku musisz także zniszczyć słuchacza, tak jak w przypadku emisji $.

Edytować:

Wolę nie używać, $rootScope.$broadcast + $scope.$onale używać $rootScope.$emit+ $rootScope.$on. $rootScope.$broadcast + $scope.$onKombi może spowodować poważne problemy z wydajnością. Dzieje się tak, ponieważ wydarzenie rozbije się na wszystkie zakresy.

Edycja 2 :

Problem rozwiązany w tej odpowiedzi został rozwiązany w wersji angular.js w wersji 1.2.7. $ broadcast pozwala teraz uniknąć bulgotania nad niezarejestrowanymi zakresami i działa tak szybko, jak $ emit.

Ved
źródło
10

Musisz użyć $ rootScope, aby wysyłać i rejestrować zdarzenia między kontrolerami w tej samej aplikacji. Wstrzyknąć kontrolerom zależność $ rootScope. Oto działający przykład.

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

Zdarzenia połączone z obiektem $ scope działają po prostu w kontrolerze właściciela. Komunikacja między kontrolerami odbywa się za pomocą $ rootScope lub Services.

Kyasar
źródło
7

Możesz zadzwonić do serwisu z kontrolera, który zwraca obietnicę, a następnie użyć go w kontrolerze. I dalej wykorzystuj $emitlub $broadcastinformuj o tym innych kontrolerów. W moim przypadku musiałem wykonywać połączenia http za pośrednictwem mojej usługi, więc zrobiłem coś takiego:

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

i moja usługa wygląda tak

    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
źródło
4

To moja funkcja:

$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
źródło
1
Myślę, że to zła praktyka, ponieważ twój rootScope będzie zagracony. Zobacz 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
źródło
2

Zakresów można używać do propagowania, wysyłania zdarzeń do potomków zakresu lub rodzica.

$ emit - propaguje zdarzenie do rodzica. $ broadcast - rozpowszechnia wydarzenie wśród dzieci. $ on - metoda nasłuchiwania zdarzeń, propagowana przez $ emit i $ broadcast.

przykładowy 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>

przykład app.js :

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

Tutaj możesz przetestować kod: http://jsfiddle.net/zp6v0rut/41/

Wasyl Gutnyk
źródło
2

Poniższy kod pokazuje dwa kontrolery podrzędne, z których zdarzenia są wysyłane w górę do kontrolera nadrzędnego (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
źródło
0

Zgodnie z dokumentacją zdarzenia angularjs koniec odbiorczy powinien zawierać argumenty o strukturze podobnej do

@params

- Zdarzenie {Object} to obiekt zdarzenia zawierający informacje o zdarzeniu

- Argumenty {Object} przekazywane przez odbiorcę (pamiętaj, że może to być tylko jeden, dlatego lepiej zawsze wysyłać obiekt słownika)

$scope.$on('fooEvent', function (event, args) { console.log(args) }); Z twojego kodu

Również jeśli próbujesz uzyskać udostępnioną informację dla różnych kontrolerów, istnieje inny sposób, aby to osiągnąć, a mianowicie usługi kątowe. Ponieważ usługi są singletonami, informacje można przechowywać i pobierać między kontrolerami. Po prostu utwórz getter i ustawiaj funkcje w tej usłudze, ujawniaj te funkcje, twórz zmienne globalne w usłudze i używaj ich do przechowywania informacji

Wajih Siddiqui
źródło
0

Najłatwiejszy sposób:

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
źródło