$ on i $ broadcast in angular


282

Mam footerController i codeScannerController z różnymi widokami.

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

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

Kiedy <li>kliknę na footer.html, powinienem pobrać to zdarzenie w codeScannerController.

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

Myślę, że można to zrealizować za pomocą $oni $broadcast, ale nie wiem jak i nigdzie nie mogę znaleźć przykładów.

Odpowiedzi:


631

Jeśli chcesz $broadcastużyć $rootScope:

$scope.startScanner = function() {

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

A następnie, aby otrzymać, użyj $scopekontrolera:

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

    // do what you want to do
});

Jeśli chcesz, możesz przekazać argumenty, gdy $broadcast:

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

A następnie otrzymaj je:

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

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

Dokumentacja tego w dokumentach dotyczących zakresu .


2
Możesz nazwać wydarzenie dowolnie.
Davin Tryon

5
Upewnij się, że masz $ scope. $ Apply (); twoje zmiany!
Ismail

4
@Ismail Dlaczego ... i gdzie?
Jaans

7
Czy są jakieś zalecane praktyki przechowywania gdzieś tych ciągów zamiast zakodowania nadawanej wiadomości?
rperryng

8
@Ismail $scope.$apply()jest konieczny tylko podczas zmiany modelu poza strukturą kątową (jak w setTimeout, wywołanie zwrotne okna dialogowego lub wywołanie zwrotne ajax), innymi słowy, $apply()jest już wyzwalany po zakończeniu całego kodu .$on().
th3uiguy

97

Po pierwsze, krótki opis $on(), $broadcast()a$emit() :

  • .$on(name, listener) - Słucha określonego zdarzenia przez dane name
  • .$broadcast(name, args)- Transmituj wydarzenie przez $scopewszystkie dzieci
  • .$emit(name, args)- Emituj wydarzenie w górę $scopehierarchii do wszystkich rodziców, w tym$rootScope

Na podstawie następującego kodu HTML ( pełny przykład tutaj ):

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

Wystrzelone zdarzenia będą przebiegać $scopesnastępująco:

  • Transmisja 1 - będzie widoczny tylko dla Kontrolera 1 $scope
  • Emit 1 - będą widoczne przez kontrolera 1 $scopenastępnie$rootScope
  • Transmisja 2 - będzie widoczny dla Kontrolera 2, $scopea następnie Kontrolera 3$scope
  • Emit 2 - będą widoczne przez kontrolera 2 $scopenastępnie$rootScope
  • Transmisja 3 - będzie widoczny tylko dla Kontrolera 3 $scope
  • Emit 3 - będzie postrzegana przez kontrolera 3 $scope, kontrolera 2 $scopeówczesnego$rootScope
  • Broadcast Root - będą widoczne przez $rootScopei $scopewszystkich sterowników (1, 2, a następnie 3)
  • Emit Root - będzie widoczny tylko dla $rootScope

JavaScript do wyzwalania zdarzeń (znowu możesz zobaczyć działający przykład tutaj ):

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

jak mogę sobie wyobrazić hierarchię mojej aplikacji na podanym przez ciebie przykładzie. Jak kontroler może być rodzicem lub dzieckiem? Próbuję powiedzieć, że mam szereg stanów, np. LoginCtrl -> homeCrl -> notyfikacjaCtrl i tak dalej.
HIRA THAKUR,

26

Jedną rzeczą, którą powinieneś wiedzieć jest to, że przedrostek $ odnosi się do metody kątowej, przedrostki $$ odnoszą się do metod kątowych, których powinieneś unikać.

poniżej znajduje się przykładowy szablon i jego kontrolery. Zbadamy, w jaki sposób $ broadcast / $ on może pomóc nam osiągnąć to, czego chcemy.

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

Kontrolery są

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

    }
});

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

});

Moje pytanie brzmi: w jaki sposób przekazujesz nazwę drugiemu kontrolerowi, gdy użytkownik kliknie rejestr? Możesz wymyślić wiele rozwiązań, ale tym, którego będziemy używać, jest użycie $ broadcast i $ on.

$ broadcast vs $ emit

Którego powinniśmy użyć? $ broadcast prześle kanał do wszystkich elementów domena potomnego, a $ emit skieruje kanał w przeciwnym kierunku do wszystkich elementów przodka.

Najlepszym sposobem uniknięcia decyzji między $ emit lub $ broadcast jest kanał z $ rootScope i użycie $ broadcast do wszystkich jego dzieci. Co znacznie ułatwia naszą sprawę, ponieważ elementami domowymi są rodzeństwo.

Dodanie $ rootScope i pozwala na emisję $

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

Zauważ, że dodaliśmy $ rootScope i teraz używamy $ broadcast (broadcastName, argumenty). Dla broadcastName chcemy nadać mu unikalną nazwę, abyśmy mogli złapać tę nazwę w naszym secondCtrl. Wybrałem BOOM! dla żartu. Drugi argument „argumenty” pozwala nam przekazywać wartości do słuchaczy.

Odbieranie naszej transmisji

W naszym drugim kontrolerze musimy skonfigurować kod, aby słuchać naszej transmisji

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

To naprawdę takie proste. Przykład na żywo

Inne sposoby osiągnięcia podobnych wyników

Staraj się unikać korzystania z tego zestawu metod, ponieważ nie jest on ani wydajny, ani łatwy w utrzymaniu, ale jest to prosty sposób na rozwiązanie problemów, które możesz mieć.

Zwykle możesz zrobić to samo, korzystając z usługi lub upraszczając kontrolery. Nie będziemy omawiać tego szczegółowo, ale pomyślałem, że po prostu wspomnę o tym dla kompletności.

Na koniec, pamiętaj, że naprawdę użytecznym przekazem do słuchania jest „$ destroy” ponownie, możesz zobaczyć, że $ oznacza, że ​​jest to metoda lub obiekt utworzony przez kody dostawcy. W każdym razie $ zniszczenie jest emitowane, gdy kontroler zostanie zniszczony, możesz tego słuchać, aby wiedzieć, kiedy kontroler zostanie usunięty.


2
Jako ostrzeżenie staraj się nie używać zbyt wielu emisji / emisji w swojej aplikacji. Mogą być bardzo trudne do zarządzania, szczególnie w dużej aplikacji, ponieważ śledzenie korzeni tych zdarzeń jest bardzo trudnym zadaniem.
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();
       };
    }); }());
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.