Utilisation de $ scope. $ Emit et $ scope. $ On

887

Comment envoyer mon $scopeobjet d'un contrôleur à un autre à l'aide des méthodes .$emitet .$on?

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

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

Cela ne fonctionne pas comme je le pense. Comment ça marche $emitet comment $on?

Paul Kononenko
la source
6
Juste pour les futurs lecteurs: ne pas utiliser $rootScopepour la diffusion / émission quand cela peut être évité.
Mistalis

Réponses:

1499

Tout d'abord, la relation de portée parent-enfant est importante. Vous avez deux possibilités pour émettre un événement:

  • $broadcast - envoie l'événement vers le bas à tous les enfants,
  • $emit - distribue l'événement vers le haut à travers la hiérarchie de portée.

Je ne sais rien de la relation entre vos contrôleurs (étendues), mais il existe plusieurs options:

  1. Si la portée de firstCtrlest parent de la secondCtrlportée, votre code devrait fonctionner en remplaçant $emitpar $broadcastdans firstCtrl:

    function firstCtrl($scope)
    {
        $scope.$broadcast('someEvent', [1,2,3]);
    }
    
    function secondCtrl($scope)
    {
        $scope.$on('someEvent', function(event, mass) { console.log(mass); });
    }
    
  2. Dans le cas où il n'y a pas de relation parent-enfant entre vos étendues, vous pouvez injecter $rootScopedans le contrôleur et diffuser l'événement à toutes les étendues enfants (c'est-à-dire également secondCtrl).

    function firstCtrl($rootScope)
    {
        $rootScope.$broadcast('someEvent', [1,2,3]);
    }
    
  3. Enfin, lorsque vous devez répartir l'événement du contrôleur enfant vers des étendues vers le haut, vous pouvez l'utiliser $scope.$emit. Si l'étendue de firstCtrlest parent de l' secondCtrlétendue:

    function firstCtrl($scope)
    {
        $scope.$on('someEvent', function(event, data) { console.log(data); });
    }
    
    function secondCtrl($scope)
    {
        $scope.$emit('someEvent', [1,2,3]);
    }
    
zbynour
la source
8
Existe-t-il un moyen de déclencher un événement d'un service vers un contrôleur?
Zlatko
29
Oui, théoriquement, vous pouvez injecter $rootScopedans votre service et diffuser l'événement à partir du service.
zbynour
13
@Zlatko Je suis presque sûr que les services par défaut sont sans portée, et vous avez besoin d'une portée pour participer au système d'événements. Vous devez donc en quelque sorte fournir une portée à votre service. $ rootScope est la solution la plus polyvalente à cela, mais si vous voulez que votre service envoie des événements à partir d'une autre étendue, votre contrôleur pourrait transmettre sa portée au service en définissant une propriété sur le service, et maintenant le service peut utiliser le portée du contrôleur. Une technique plus simple pourrait être que le contrôleur fournisse une fonction au service que le service peut appeler directement.
Oran Dennison
3
Si vous utilisez un iframe, cet article vous sera utile charemza.name/blog/posts/angularjs/iframe/…
leticia
1
Les services peuvent injecter $rootScope- mais je veux savoir que si j'émets un événement à partir d'un service (hors de $rootScope), l'événement continuera de percoler $rootScope; PARCE QUE, si $broadcastpercole vers le BAS de la hiérarchie, et vers le $emithaut - ce qui se passe entre "UP" et "DOWN" - puisque le diffuseur / émetteur est également l'auditeur (?). Que se passe-t-il si je veux que l'événement soit silencieux sur TOUTES les portées "UPWARD" et TOUS "DOWNWARD", mais seulement "audible" au même niveau que le répartiteur?
Cody
145

Je suggérerais en outre une 4ème option comme meilleure alternative aux options proposées par @zbynour.

Utilisez $rootScope.$emitplutôt $rootScope.$broadcastqu'indépendamment de la relation entre le transmetteur et le contrôleur récepteur. De cette façon, l'événement reste dans l'ensemble de $rootScope.$$listenerstandis qu'avec $rootScope.$broadcastl'événement se propage à tous les enfants, les portées, dont la plupart ne seront probablement pas à l'écoute de cet événement de toute façon. Et bien sûr, du côté du contrôleur récepteur que vous utilisez $rootScope.$on.

Pour cette option, vous devez vous rappeler de détruire les écouteurs rootScope du contrôleur:

var unbindEventHandler = $rootScope.$on('myEvent', myHandler);
$scope.$on('$destroy', function () {
  unbindEventHandler();
});
Thalis K.
la source
3
Cela servirait alors essentiellement de bus d'événement central, n'est-ce pas?
jusopi
5
Dans un sens oui, l'avantage est d'éviter la propagation des événements.
Thalis K.
3
@ThalisK. merci pour cette option. Il évite la propagation mais en revanche il nécessite une $rootScopeinjection dans les contrôleurs (ce qui n'est pas nécessaire en général). Mais sûrement une autre option, merci!
zbynour
77
Attention, $ rootScope vit éternellement. Si votre contrôleur est exécuté deux fois, tout $ rootScope. $ À l'intérieur sera exécuté deux fois, et les événements interceptés entraîneront un rappel appelé deux fois. Si vous utilisez $ scope. $ On à la place, le rappel sera détruit avec votre contrôleur implicitement par AngularJS.
Filip Sobczak
1
Selon le commentaire @FilipSobczak, vous pouvez éviter ce comportement indésirable en dissociant le gestionnaire sur l'événement $ destroy avec le code suivant jsfiddle.net/ndqexjsg/1
Krzysztof Grzybek
111

Comment envoyer mon objet $ scope d'un contrôleur à un autre en utilisant les méthodes. $ Emit et. $ On?

Vous pouvez envoyer tout objet de votre choix dans la hiérarchie de votre application, y compris $ scope .

Voici une idée rapide sur la façon dont la diffusion et Emit travail.

Remarquez les nœuds ci-dessous; tous imbriqués dans le nœud 3. Vous utilisez la diffusion et émettez lorsque vous avez ce scénario.

Remarque: Le nombre de chaque nœud dans cet exemple est arbitraire; ce pourrait facilement être le numéro un; le numéro deux; ou même le nombre 1.348. Chaque numéro n'est qu'un identifiant pour cet exemple. Le but de cet exemple est de montrer l'imbrication des contrôleurs / directives angulaires.

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

Découvrez cet arbre. Comment répondez-vous aux questions suivantes?

Note: Il y a d' autres façons de répondre à ces questions, mais ici , nous allons discuter de diffusion et Emit . De plus, lors de la lecture du texte ci-dessous, supposez que chaque numéro a son propre fichier (directive, contrôleur) ex one.js, two.js, three.js.

Comment le nœud 1 parle-t-il au nœud 3 ?

Dans le fichier one.js

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

Dans le fichier three.js - le nœud le plus élevé de tous les nœuds enfants nécessaires pour communiquer.

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

Comment le nœud 2 parle-t-il au nœud 3?

Dans le fichier two.js

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

Dans le fichier three.js - le nœud le plus élevé de tous les nœuds enfants nécessaires pour communiquer.

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

Comment le nœud 3 parle-t-il au nœud 1 et / ou au nœud 2?

Dans le fichier three.js - le nœud le plus élevé de tous les nœuds enfants nécessaires pour communiquer.

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

Dans le fichier one.js && two.js, quel fichier vous voulez attraper le message ou les deux.

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

Comment le nœud 2 parle-t-il au nœud 1?

Dans le fichier two.js

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

Dans le fichier three.js - le nœud le plus élevé de tous les nœuds enfants nécessaires pour communiquer.

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

Dans le fichier one.js

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

TOUTEFOIS

Lorsque tous ces nœuds enfants imbriqués essaient de communiquer comme cela, vous verrez rapidement de nombreux $ on , $ broadcast et $ emit .

Voici ce que j'aime faire.

Dans le NŒUD PARENT supérieur ( 3 dans ce cas ...), qui peut être votre contrôleur parent ...

Donc, dans le fichier three.js

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

Maintenant, dans n'importe lequel des nœuds enfants, il vous suffit d' émettre $ le message ou de l'attraper en utilisant $ on .

REMARQUE: Il est normalement assez facile de traverser une conversation dans un chemin imbriqué sans utiliser $ emit , $ broadcast ou $ on , ce qui signifie que la plupart des cas d'utilisation sont lorsque vous essayez de faire communiquer le nœud 1 avec le nœud 2 ou vice versa.

Comment le nœud 2 parle-t-il au nœud 1?

Dans le fichier two.js

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

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

Dans le fichier three.js - le nœud le plus élevé de tous les nœuds enfants nécessaires pour communiquer.

Nous avons déjà traité celui-ci, vous vous en souvenez?

Dans le fichier one.js

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

Vous devrez toujours utiliser $ on avec chaque valeur spécifique que vous souhaitez attraper, mais maintenant vous pouvez créer ce que vous voulez dans l'un des nœuds sans avoir à vous soucier de la façon de faire passer le message à travers l'écart du nœud parent pendant que nous interceptons et diffusons le pushChangesToAllNodes générique .

J'espère que cela t'aides...

SoEzPz
la source
comment décider lequel est 3,2 et 1?
HIRA THAKUR
Les 3, 2 et 1 sont des contrôleurs ou des directives imbriqués. Lorsque vous créez votre application, gardez à l'esprit votre imbrication et appliquez la logique ci-dessus. Par exemple, nous pourrions dire que 3 est le $ rootScope de l'application; et tout est imbriqué en dessous. 3, 2 et 1 sont arbitraires.
SoEzPz du
Grands exemples! Mais je pense toujours qu'il vaut mieux utiliser son propre répartiteur d'événements dans le parent pour communiquer un groupe de contrôleurs. Également utile pour conserver la création du répartiteur en tant que service et l'utiliser comme modèle.
DenisKolodin
1
Selon les documents angulaires sur $ 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. vous obtiendrez (comme moi) une boucle infinie si vous implémentez ctrl1 en parlant à ctrl2 avec $on('x', function(e, data) { $broadcast('x', data) })on ctrl3. Vous aurez besoin de ces lignes avant de diffuser; if (e.targetScope.$id === $scope.$id) { return; }
Renato Gama
39

Pour envoyer $scope objectd'un contrôleur à un autre, je vais discuter de $rootScope.$broadcastet $rootScope.$emitici car ils sont les plus utilisés.

Cas 1 :

$ rootScope. $ broadcast: -

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

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

$rootScopel'auditeur n'est pas détruit automatiquement. Vous devez le détruire en utilisant $destroy. Il est préférable de l'utiliser $scope.$oncar les écouteurs $scopesont détruits automatiquement, c'est-à-dire dès que $ scope est détruit.

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

Ou,

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

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

Cas 2:

$ rootScope. $ emit:

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

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

La principale différence entre $ emit et $ broadcast est que l'événement $ rootScope. $ Emit doit être écouté à l'aide de $ rootScope. $ On, car l'événement émis ne descend jamais via l'arborescence de la portée. .
Dans ce cas également, vous devez détruire l'auditeur comme dans le cas de $ broadcast.

Éditer:

Je préfère ne pas utiliser $rootScope.$broadcast + $scope.$onmais utiliser $rootScope.$emit+ $rootScope.$on. Le $rootScope.$broadcast + $scope.$oncombo peut entraîner de graves problèmes de performances. En effet, l'événement bouillonnera dans tous les domaines.

Modifier 2 :

Le problème résolu dans cette réponse a été résolu dans angular.js version 1.2.7. $ broadcast évite désormais de se propager sur des étendues non enregistrées et s'exécute aussi vite que $ emit.

Ved
la source
10

Vous devez utiliser $ rootScope pour envoyer et capturer des événements entre des contrôleurs dans la même application. Injectez la dépendance $ rootScope à vos contrôleurs. Voici un exemple de travail.

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

Les événements liés à l'objet $ scope fonctionnent simplement dans le contrôleur propriétaire. La communication entre les contrôleurs se fait via $ rootScope ou Services.

kyasar
la source
7

Vous pouvez appeler un service de votre contrôleur qui renvoie une promesse, puis l'utiliser dans votre contrôleur. Et utilisez-le $emitou $broadcastpour en informer d'autres contrôleurs. Dans mon cas, j'ai dû passer des appels http via mon service, j'ai donc fait quelque chose comme ceci:

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

et mon service ressemble à ceci

    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
la source
4

C'est ma fonction:

$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
la source
1
Je pense que c'est une mauvaise pratique car votre rootScope sera encombré. Voir 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
la source
2

Les étendues peuvent être utilisées pour propager, distribuer un événement aux enfants de l'étendue ou au parent.

$ emit - propage l'événement au parent. $ broadcast - propage l'événement aux enfants. $ on - méthode pour écouter les événements, propagée par $ emit et $ broadcast.

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

exemple app.js :

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

Ici, vous pouvez tester le code: http://jsfiddle.net/zp6v0rut/41/

Vasyl Gutnyk
la source
2

Le code ci-dessous montre les deux sous-contrôleurs d'où les événements sont envoyés vers le haut vers le contrôleur parent (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
la source
0

Selon les documents d'événement angularjs, l'extrémité de réception devrait contenir des arguments avec une structure comme

@params

- {Object} événement étant l'objet événement contenant des informations sur l'événement

- Arguments {Object} qui sont passés par l'appelé (Notez que cela ne peut être que si il est préférable d'envoyer toujours un objet dictionnaire)

$scope.$on('fooEvent', function (event, args) { console.log(args) }); De votre code

De plus, si vous essayez d'obtenir une information partagée pour être disponible sur différents contrôleurs, il existe un autre moyen d'y parvenir, à savoir les services angulaires. définir des fonctions dans ce service, exposer ces fonctions, créer des variables globales dans le service et les utiliser pour stocker les informations

Wajih Siddiqui
la source
0

La manière la plus simple:

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