Arbeiten mit $ scope. $ Emit und $ scope. $ On

887

Wie kann ich mein $scopeObjekt mit .$emitund .$onMethoden von einem Controller an einen anderen senden ?

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

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

Es funktioniert nicht so, wie ich es mir vorstelle. Wie machen $emitund $onarbeiten?

Paul Kononenko
quelle
6
Nur für zukünftige Leser: Nicht $rootScopefür Broadcast / Emit verwenden, wenn dies vermieden werden kann.
Mistalis

Antworten:

1499

Zunächst spielt die Beziehung zwischen Eltern und Kind eine Rolle. Sie haben zwei Möglichkeiten, ein Ereignis auszusenden:

  • $broadcast - schickt das Ereignis nach unten an alle untergeordneten Bereiche,
  • $emit - Versendet das Ereignis über die Bereichshierarchie nach oben.

Ich weiß nichts über Ihre Controller-Beziehung (Scopes), aber es gibt verschiedene Möglichkeiten:

  1. Wenn der Gültigkeitsbereich von firstCtrldem secondCtrlGültigkeitsbereich übergeordnet ist , sollte Ihr Code durch Ersetzen $emitdurch $broadcastin firstCtrl:

    function firstCtrl($scope)
    {
        $scope.$broadcast('someEvent', [1,2,3]);
    }
    
    function secondCtrl($scope)
    {
        $scope.$on('someEvent', function(event, mass) { console.log(mass); });
    }
  2. Falls zwischen Ihren Bereichen keine Eltern-Kind-Beziehung besteht, können Sie sie $rootScopein den Controller einfügen und das Ereignis an alle untergeordneten Bereiche (dh auch secondCtrl) senden .

    function firstCtrl($rootScope)
    {
        $rootScope.$broadcast('someEvent', [1,2,3]);
    }
  3. Wenn Sie das Ereignis vom untergeordneten Controller an Bereiche nach oben senden müssen, können Sie es schließlich verwenden $scope.$emit. Wenn der Gültigkeitsbereich firstCtrlvon dem secondCtrlGültigkeitsbereich übergeordnet ist :

    function firstCtrl($scope)
    {
        $scope.$on('someEvent', function(event, data) { console.log(data); });
    }
    
    function secondCtrl($scope)
    {
        $scope.$emit('someEvent', [1,2,3]);
    }
zbynour
quelle
8
Gibt es eine Möglichkeit, ein Ereignis von einem Dienst an einen Controller auszulösen?
Zlatko
29
Ja, theoretisch könnten Sie $rootScopein Ihren Dienst einspeisen und das Ereignis vom Dienst aus senden.
Zbynour
13
@Zlatko Ich bin mir ziemlich sicher, dass Dienste standardmäßig ohne Gültigkeitsbereich sind und Sie einen Gültigkeitsbereich benötigen, um am Ereignissystem teilnehmen zu können. Sie müssen also irgendwie einen Bereich für Ihren Service bereitstellen. $ rootScope ist die allgemeinste Lösung dafür. Wenn Sie jedoch möchten, dass Ihr Dienst Ereignisse aus einem anderen Bereich sendet, kann Ihr Controller seinen Bereich an den Dienst übergeben, indem er eine Eigenschaft für den Dienst festlegt. Jetzt kann der Dienst die verwenden Umfang des Controllers. Eine einfachere Technik könnte darin bestehen, dass die Steuerung dem Dienst eine Funktion bereitstellt, die der Dienst direkt aufrufen kann.
Oran Dennison
3
Wenn Sie einen Iframe verwenden, ist dieser Artikel hilfreich. Charemza.name/blog/posts/angularjs/iframe/…
leticia
1
Dienste können injiziert werden $rootScope- aber ich möchte wissen $rootScope, dass das Ereignis immer noch versickert , wenn ich ein Ereignis von einem Dienst (aus ) aussende $rootScope. WEIL, wenn $broadcastdie Hierarchie nach unten sickert und nach oben $emitsickert - was passiert ZWISCHEN "AUF" und "UNTEN" - da der Sender / Sender auch der Zuhörer ist (?). Was ist, wenn das Ereignis für ALLE "UPWARD" - und ALL "DOWNWARD" -Bereiche stumm sein soll, aber nur auf derselben Ebene wie der Dispatcher "hörbar" sein soll?
Cody
145

Ich würde zusätzlich eine 4. Option als bessere Alternative zu den von @zbynour vorgeschlagenen Optionen vorschlagen.

Verwenden Sie $rootScope.$emitanstatt $rootScope.$broadcastunabhängig von der Beziehung zwischen Sende- und Empfangscontroller. Auf diese Weise bleibt das Ereignis innerhalb des Satzes von, $rootScope.$$listenerswährend sich $rootScope.$broadcastdas Ereignis auf alle untergeordneten Bereiche ausbreitet, von denen die meisten wahrscheinlich sowieso keine Zuhörer dieses Ereignisses sein werden. Und natürlich verwenden Sie am Ende des empfangenden Controllers nur $rootScope.$on.

Für diese Option müssen Sie daran denken, die rootScope-Listener des Controllers zu zerstören:

var unbindEventHandler = $rootScope.$on('myEvent', myHandler);
$scope.$on('$destroy', function () {
  unbindEventHandler();
});
Thalis K.
quelle
3
Dies würde dann grundsätzlich als zentraler Eventbus dienen, richtig?
Jusopi
5
In gewissem Sinne ja, der Vorteil ist, dass Sie die Weitergabe von Ereignissen vermeiden.
Thalis K.
3
@ThalisK. Vielen Dank für diese Option. Es vermeidet die Ausbreitung, erfordert jedoch andererseits die $rootScopeInjektion in Steuerungen (was im Allgemeinen nicht benötigt wird). Aber sicherlich eine andere Option, danke!
Zbynour
77
Beachten Sie, dass $ rootScope für immer lebt. Wenn Ihr Controller zweimal ausgeführt wird, wird jedes darin enthaltene $ rootScope. $ Zweimal ausgeführt, und abgefangene Ereignisse führen zu einem zweimaligen Rückruf. Wenn Sie stattdessen $ scope. $ On verwenden, wird der Rückruf zusammen mit Ihrem Controller implizit von AngularJS zerstört.
Filip Sobczak
1
Laut @FilipSobczak Kommentar können Sie dieses unerwünschte Verhalten vermeiden, indem Sie den Handler für $ destroy event mit dem folgenden Code entbinden
Krzysztof Grzybek
111

Wie kann ich mein $ scope-Objekt mit den Methoden. $ Emit und. $ On von einem Controller an einen anderen senden?

Sie können jedes gewünschte Objekt innerhalb der Hierarchie Ihrer App senden, einschließlich $ scope .

Hier ist eine kurze Vorstellung davon, wie Broadcast und Emit funktionieren.

Beachten Sie die Knoten unten; Alle in Knoten 3 verschachtelt. In diesem Szenario verwenden Sie Broadcast und Emit .

Hinweis: Die Nummer jedes Knotens in diesem Beispiel ist beliebig. es könnte leicht die Nummer eins sein; die Nummer zwei; oder sogar die Nummer 1.348. Jede Nummer ist nur eine Kennung für dieses Beispiel. In diesem Beispiel soll die Verschachtelung von Angular-Controllern / Direktiven gezeigt werden.

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

Schauen Sie sich diesen Baum an. Wie beantworten Sie die folgenden Fragen?

Hinweis: Es gibt andere Möglichkeiten, diese Fragen zu beantworten. Hier werden jedoch die Übertragung und die Übertragung erläutert . Wenn Sie den folgenden Text lesen, nehmen Sie an, dass jede Nummer eine eigene Datei (Direktive, Controller) hat, z. B. one.js, two.js, three.js.

Wie spricht Knoten 1 mit Knoten 3 ?

In der Datei one.js

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

In Datei three.js - der oberste Knoten zu allen untergeordneten Knoten, die für die Kommunikation benötigt werden.

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

Wie spricht Knoten 2 mit Knoten 3?

In der Datei two.js

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

In Datei three.js - der oberste Knoten zu allen untergeordneten Knoten, die für die Kommunikation benötigt werden.

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

Wie spricht Knoten 3 mit Knoten 1 und / oder Knoten 2?

In Datei three.js - der oberste Knoten zu allen untergeordneten Knoten, die für die Kommunikation benötigt werden.

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

In der Datei one.js && two.js die Datei, in der Sie die Nachricht abfangen möchten, oder beides.

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

Wie spricht Knoten 2 mit Knoten 1?

In der Datei two.js

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

In Datei three.js - der oberste Knoten zu allen untergeordneten Knoten, die für die Kommunikation benötigt werden.

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

In der Datei one.js

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

JEDOCH

Wenn alle diese verschachtelten untergeordneten Knoten versuchen, auf diese Weise zu kommunizieren, werden Sie schnell viele $ ons , $ Broadcasts und $ emits sehen .

Folgendes mache ich gerne.

Im obersten ELTERNKNOTEN ( in diesem Fall 3 ...), der möglicherweise Ihr übergeordneter Controller ist ...

Also, in Datei drei.js

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

Jetzt müssen Sie in einem der untergeordneten Knoten nur noch $ die Nachricht ausgeben oder sie mit $ on abfangen .

HINWEIS: Normalerweise ist es recht einfach, in einem verschachtelten Pfad zu sprechen, ohne $ emit , $ Broadcast oder $ on zu verwenden . Dies bedeutet, dass die meisten Anwendungsfälle auftreten, wenn Sie versuchen, Knoten 1 zur Kommunikation mit Knoten 2 zu bewegen oder umgekehrt.

Wie spricht Knoten 2 mit Knoten 1?

In der Datei two.js

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

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

In Datei three.js - der oberste Knoten zu allen untergeordneten Knoten, die für die Kommunikation benötigt werden.

Wir haben das schon erledigt, erinnerst du dich?

In der Datei one.js

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

Sie müssen weiterhin $ on für jeden bestimmten Wert verwenden, den Sie abfangen möchten. Jetzt können Sie jedoch in jedem der Knoten beliebige Werte erstellen, ohne sich Gedanken darüber machen zu müssen, wie die Nachricht beim Abfangen und Senden über die Lücke des übergeordneten Knotens übertragen werden kann die generischen pushChangesToAllNodes .

Hoffe das hilft...

SoEzPz
quelle
Wie kann man entscheiden, welches 3,2 und 1 ist?
HIRA THAKUR
Die 3, 2 und 1 sind entweder verschachtelte Controller oder Direktiven. Denken Sie beim Erstellen Ihrer App an Ihre Verschachtelung und wenden Sie die obige Logik an. Als Beispiel könnten wir sagen, 3 ist das $ rootScope der Anwendung; und alles ist darunter verschachtelt. 3, 2 und 1 sind beliebig.
SoEzPz
Tolle Beispiele! Aber ich denke immer noch, dass es besser ist, einen eigenen Event-Dispatcher im übergeordneten Element zu verwenden, um eine Gruppe von Controllern zu kommunizieren. Auch nützlich, um die Erstellung des Dispatchers als Service beizubehalten und als Muster zu verwenden.
DenisKolodin
1
Laut eckigen Dokumenten auf $ Broadcast erhalten The event life cycle starts at the scope on which $broadcast was called. All listeners listening for name event on this scope get notified. Sie (wie ich) daher eine Endlosschleife, wenn Sie Strg1 implementieren und mit Strg2 mit Strg3 sprechen $on('x', function(e, data) { $broadcast('x', data) }). Sie benötigen diese Zeilen vor der Ausstrahlung. if (e.targetScope.$id === $scope.$id) { return; }
Renato Gama
39

Um $scope objectvon einem Controller zu einem anderen zu senden , werde ich darüber $rootScope.$broadcastund $rootScope.$emithier diskutieren , wie sie am häufigsten verwendet werden.

Fall 1 :

$ rootScope. $ Broadcast: -

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

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

$rootScopeListener werden nicht automatisch zerstört. Sie müssen es mit zerstören $destroy. Es ist besser zu verwenden, $scope.$onwenn Listener eingeschaltet $scopewerden, dh sobald $ scope zerstört wird.

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

Oder,

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

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

Fall 2:

$ rootScope. $ emit:

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

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

Der Hauptunterschied zwischen $ emit und $ Broadcast besteht darin, dass das Ereignis $ rootScope. $ Emit mit $ rootScope. $ On abgehört werden muss, da das ausgegebene Ereignis niemals über den Bereichsbaum herunterkommt. .
In diesem Fall müssen Sie auch den Listener wie im Fall von $ Broadcast zerstören.

Bearbeiten:

Ich bevorzuge es nicht zu benutzen, $rootScope.$broadcast + $scope.$onsondern zu benutzen $rootScope.$emit+ $rootScope.$on. Die $rootScope.$broadcast + $scope.$onKombination kann schwerwiegende Leistungsprobleme verursachen. Das liegt daran, dass das Ereignis durch alle Bereiche sprudelt.

Bearbeiten 2 :

Das in dieser Antwort angesprochene Problem wurde in angle.js Version 1.2.7 behoben. $ Broadcast verhindert jetzt das Sprudeln über nicht registrierte Bereiche und läuft genauso schnell wie $ emit.

Ved
quelle
10

Sie müssen $ rootScope verwenden, um Ereignisse zwischen Controllern in derselben App zu senden und zu erfassen. Fügen Sie Ihren Controllern die Abhängigkeit $ rootScope ein. Hier ist ein Arbeitsbeispiel.

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

Mit dem $ scope-Objekt verknüpfte Ereignisse funktionieren nur im Eigentümer-Controller. Die Kommunikation zwischen Controllern erfolgt über $ rootScope oder Services.

Kyasar
quelle
7

Sie können einen Dienst von Ihrem Controller aus anrufen, der ein Versprechen zurückgibt, und ihn dann in Ihrem Controller verwenden. Und weiter verwenden $emitoder $broadcastandere Controller darüber informieren. In meinem Fall musste ich über meinen Dienst http-Anrufe tätigen, also habe ich so etwas gemacht:

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

und mein Service sieht so aus

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

Das ist meine Funktion:

$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
quelle
1
Ich denke, dies ist eine schlechte Praxis, da Ihr rootScope überladen sein wird. Siehe 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
quelle
2

Bereiche können verwendet werden, um Ereignisse an die untergeordneten oder übergeordneten Bereiche des Bereichs weiterzugeben und zu versenden.

$ emit - gibt das Ereignis an das übergeordnete Ereignis weiter. $ Broadcast - gibt das Ereignis an Kinder weiter. $ on - Methode zum Abhören der Ereignisse, die von $ emit und $ Broadcast verbreitet werden.

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

Beispiel app.js :

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

Hier können Sie Code testen: http://jsfiddle.net/zp6v0rut/41/

Vasyl Gutnyk
quelle
2

Der folgende Code zeigt die beiden Subcontroller, von denen aus die Ereignisse nach oben an den übergeordneten Controller (rootScope) gesendet werden.

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

Gemäß den Angularjs-Ereignisdokumenten sollte das empfangende Ende Argumente mit einer Struktur wie enthalten

@params

- {Object} -Ereignis ist das Ereignisobjekt, das Informationen zum Ereignis enthält

- {Objekt} Argumente, die vom Angerufenen übergeben werden (Beachten Sie, dass dies nur eines sein kann, um ein Wörterbuchobjekt immer besser einzusenden)

$scope.$on('fooEvent', function (event, args) { console.log(args) }); Aus Ihrem Code

Auch wenn Sie versuchen, eine gemeinsam genutzte Information für verschiedene Controller verfügbar zu machen, gibt es eine andere Möglichkeit, dies zu erreichen, nämlich Winkel-Services. Da es sich bei den Services um Singletons handelt, können Informationen zwischen Controllern gespeichert und abgerufen werden. Erstellen Sie einfach Getter und Setter-Funktionen in diesem Dienst, machen diese Funktionen verfügbar, erstellen globale Variablen im Dienst und verwenden sie zum Speichern der Informationen

Wajih Siddiqui
quelle
0

Der einfachste Weg :

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
quelle