$ on et $ diffusé en angulaire

282

J'ai un footerController et un codeScannerController avec des vues différentes.

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

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

Lorsque je clique sur un <li>fichier footer.html, je devrais obtenir cet événement dans codeScannerController.

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

Je pense que cela peut être réalisé avec $onet $broadcast, mais je ne sais pas comment et ne peux trouver d'exemples nulle part.

Alice Polansky
la source

Réponses:

631

Si vous souhaitez $broadcastutiliser $rootScope:

$scope.startScanner = function() {

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

Et puis pour recevoir, utilisez le $scopede votre manette:

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

    // do what you want to do
});

Si vous le souhaitez, vous pouvez passer des arguments lorsque vous $broadcast:

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

Et puis recevez-les:

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

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

Documentation pour cela dans les documents Scope .

Davin Tryon
la source
2
Vous pouvez nommer l'événement comme bon vous semble.
Davin Tryon
5
Assurez-vous que vous êtes $ scope. $ Apply (); vos changements!
Ismail
4
@Ismail Pourquoi ... et où?
Jaans
7
Existe-t-il des pratiques recommandées pour stocker ces chaînes quelque part plutôt que de coder en dur le message diffusé?
rperryng
8
@Ismail $scope.$apply()n'est nécessaire que lorsque vous modifiez le modèle en dehors du cadre angulaire (comme dans un setTimeout, un rappel de dialogue ou un rappel ajax), en d'autres termes, il $apply()est déjà déclenché une fois que tout le code .$on()est terminé.
th3uiguy
97

Tout d' abord, une brève description de $on(), $broadcast()et$emit() :

  • .$on(name, listener) - Écoute un événement spécifique par une donnée name
  • .$broadcast(name, args)- Diffusez un événement à travers $scopetous les enfants
  • .$emit(name, args)- Émettre un événement dans la $scopehiérarchie à tous les parents, y compris le$rootScope

Basé sur le code HTML suivant (voir l'exemple complet ici ):

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

Les événements déclenchés se déplacent $scopescomme suit:

  • Diffusion 1 - Ne sera vu que par le contrôleur 1 $scope
  • Emit 1 - sera vu par le contrôleur 1 $scopepuis$rootScope
  • Diffusion 2 - sera vu par le contrôleur 2 $scopepuis le contrôleur 3$scope
  • Emit 2 - sera vu par le contrôleur 2 $scopepuis$rootScope
  • Diffusion 3 - Ne sera vu que par le contrôleur 3 $scope
  • Emit 3 - sera vu par le contrôleur 3 $scope, le contrôleur 2 $scopepuis$rootScope
  • Broadcast Root - sera vu par $rootScopeet $scopede tous les contrôleurs (1, 2 puis 3)
  • Emit Root - ne sera vu que par $rootScope

JavaScript pour déclencher des événements (encore une fois, vous pouvez voir un exemple de travail ici ):

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
la source
comment puis-je imaginer la hiérarchie de mon application avec l'exemple que vous avez donné. Comment un contrôleur peut-il être un parent ou un enfant. ?? Ce que j'essaie de dire, c'est que j'ai une série d'états, par exemple. LoginCtrl -> homeCrl -> notificationCtrl et ainsi de suite.
HIRA THAKUR du
26

Une chose que vous devez savoir est que $ prefix fait référence à une méthode angulaire, $$ prefixes fait référence aux méthodes angulaires que vous devez éviter d'utiliser.

ci-dessous est un exemple de modèle et ses contrôleurs, nous explorerons comment $ broadcast / $ on peut nous aider à réaliser ce que nous voulons.

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

Les contrôleurs sont

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

    }
});

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

});

Ma question est de savoir comment transmettre le nom au deuxième contrôleur lorsqu'un utilisateur clique sur s'inscrire? Vous pouvez trouver plusieurs solutions, mais celle que nous allons utiliser utilise $ broadcast et $ on.

$ broadcast vs $ emit

Que devrions-nous utiliser? $ broadcast canalisera tous les éléments dom enfants et $ emit canalisera la direction opposée vers tous les éléments dom ancêtre.

La meilleure façon d'éviter de choisir entre $ emit ou $ broadcast est de canaliser depuis $ rootScope et d'utiliser $ broadcast à tous ses enfants. Ce qui rend notre cas beaucoup plus facile puisque nos éléments dom sont des frères et sœurs.

Ajout de $ rootScope et laisse $ broadcast

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

Notez que nous avons ajouté $ rootScope et maintenant nous utilisons $ broadcast (broadcastName, arguments). Pour broadcastName, nous voulons lui donner un nom unique afin que nous puissions saisir ce nom dans notre secondCtrl. J'ai choisi BOOM! juste pour le fun. Le second argument «arguments» nous permet de transmettre des valeurs aux écouteurs.

Recevoir notre émission

Dans notre deuxième contrôleur, nous devons configurer du code pour écouter notre diffusion

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

C'est vraiment aussi simple que cela. Exemple en direct

Autres moyens d'obtenir des résultats similaires

Essayez d'éviter d'utiliser cette suite de méthodes car elle n'est ni efficace ni facile à entretenir, mais c'est un moyen simple de résoudre les problèmes que vous pourriez avoir.

Vous pouvez généralement faire la même chose en utilisant un service ou en simplifiant vos contrôleurs. Nous ne discuterons pas de cela en détail, mais je pensais simplement le mentionner pour être complet.

Enfin, gardez à l'esprit qu'une diffusion vraiment utile à écouter est à nouveau «$ destroy», vous pouvez voir le $ signifie que c'est une méthode ou un objet créé par les codes du fournisseur. Quoi qu'il en soit, $ destroy est diffusé lorsqu'un contrôleur est détruit, vous pouvez l'écouter pour savoir quand votre contrôleur est supprimé.

Yang Li
la source
2
Comme avertissement, essayez de ne pas utiliser trop d'émissions / émissions dans votre application. Ils peuvent être extrêmement difficiles à gérer, en particulier dans une grande application, car le traçage des racines de ces événements est une tâche très difficile.
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();
       };
    }); }());
Sablonneux
la source