webentwicklung-frage-antwort-db.com.de

Wie kann ich in AngularJS zwischen Controllern kommunizieren?

Wie kommuniziert der Controller richtig?

Ich verwende derzeit einen schrecklichen Fudge mit window:

function StockSubgroupCtrl($scope, $http) {
    $scope.subgroups = [];
    $scope.handleSubgroupsLoaded = function(data, status) {
        $scope.subgroups = data;
    }
    $scope.fetch = function(prod_grp) {
        $http.get('/api/stock/groups/' + prod_grp + '/subgroups/').success($scope.handleSubgroupsLoaded);
    }
    window.fetchStockSubgroups = $scope.fetch;
}

function StockGroupCtrl($scope, $http) {
    ...
    $scope.select = function(prod_grp) {
        $scope.selectedGroup = prod_grp;
        window.fetchStockSubgroups(prod_grp);
    }
}
463
fadedbee

Edit: Das in dieser Antwort angesprochene Problem wurde in angle.js Version 1.2.7 behoben. $broadcast vermeidet das Sprudeln über nicht registrierte Bereiche und läuft genauso schnell wie $ emit .$broadcast performances are identical to $emit with angular 1.2.16

So, jetzt können Sie:

  • verwenden Sie $broadcast aus dem $rootScope
  • hören Sie mit $onvon dem lokalen $scope, der über das Ereignis Bescheid wissen muss

Originalantwort unten

Ich empfehle dringend, $rootScope.$broadcast + $scope.$on nicht zu verwenden, sondern $rootScope.$emit + $rootScope.$on. Ersteres kann zu ernsthaften Leistungsproblemen führen, die von @numan aufgeworfen werden. Das liegt daran, dass das Ereignis durch alle Bereiche sprudelt. 

Letzteres (mit $rootScope.$emit + $rootScope.$on) leidet jedoch an not und kann daher als schneller Kommunikationskanal verwendet werden!

Aus der Winkeldokumentation von $emit:

Sendet einen Ereignisnamen durch die Bereichshierarchie nach oben und benachrichtigt die registrierten

Da es keinen Bereich oberhalb von $rootScope gibt, gibt es kein Sprudeln. Es ist absolut sicher, $rootScope.$emit()/$rootScope.$on() als EventBus zu verwenden.

Es gibt jedoch ein Problem bei der Verwendung von Controllern. Wenn Sie direkt von einem Controller aus an $rootScope.$on() binden, müssen Sie die Bindung selbst bereinigen, wenn Ihr lokaler $scope zerstört wird. Dies liegt daran, dass Controller (im Gegensatz zu Diensten) während der gesamten Lebensdauer einer Anwendung mehrmals instanziiert werden können, was zu Bindungen führen würde, die schließlich zu Speicherlecks führen.

Hören Sie zum Aufheben der Registrierung einfach das $scope-Ereignis von $destroy und rufen Sie die von $rootScope.$on zurückgegebene Funktion auf.

angular
    .module('MyApp')
    .controller('MyController', ['$scope', '$rootScope', function MyController($scope, $rootScope) {

            var unbind = $rootScope.$on('someComponent.someCrazyEvent', function(){
                console.log('foo');
            });

            $scope.$on('$destroy', unbind);
        }
    ]);

Ich würde sagen, das ist nicht wirklich eine winkelspezifische Sache, da es auch für andere EventBus-Implementierungen gilt, dass Sie Ressourcen bereinigen müssen.

Allerdings machen Sie can Ihr Leben für diese Fälle einfacher. Zum Beispiel könnten Sie den Patch $rootScope mit einem Affenflecken versehen und ihm einen $onRootScope geben, der Ereignisse abonniert, die auf dem $rootScope ausgegeben werden, aber auch den Handler direkt bereinigt, wenn der lokale $scope zerstört wird.

Der sauberste Weg, um den $rootScope für die Bereitstellung einer solchen $onRootScope-Methode zu verwenden, würde durch einen Dekorierer erfolgen (ein Laufblock wird es wahrscheinlich auch gut machen, aber pssst, sag es niemandem)

Um sicherzustellen, dass die $onRootScope-Eigenschaft beim Auflisten über $scope nicht unerwartet angezeigt wird, verwenden wir Object.defineProperty() und setzen enumerable auf false. Denken Sie daran, dass Sie möglicherweise eine ES5-Scheibe benötigen.

angular
    .module('MyApp')
    .config(['$provide', function($provide){
        $provide.decorator('$rootScope', ['$delegate', function($delegate){

            Object.defineProperty($delegate.constructor.prototype, '$onRootScope', {
                value: function(name, listener){
                    var unsubscribe = $delegate.$on(name, listener);
                    this.$on('$destroy', unsubscribe);

                    return unsubscribe;
                },
                enumerable: false
            });


            return $delegate;
        }]);
    }]);

Mit dieser Methode kann der Controller-Code von oben vereinfacht werden:

angular
    .module('MyApp')
    .controller('MyController', ['$scope', function MyController($scope) {

            $scope.$onRootScope('someComponent.someCrazyEvent', function(){
                console.log('foo');
            });
        }
    ]);

Als letztes Ergebnis empfehle ich Ihnen dringend, $rootScope.$emit + $scope.$onRootScope zu verwenden.

Übrigens, ich versuche das Winkelteam davon zu überzeugen, das Problem im Winkelkern zu lösen. Hier gibt es eine Diskussion: https://github.com/angular/angular.js/issues/4574

Hier ist eine Jsperf, die zeigt, wie viel Leistung $broadcast in einem anständigen Szenario mit nur 100 $scopes zur Tabelle bringt.

http://jsperf.com/rootscope-emit-vs-rootscope-broadcast

jsperf results

452
Christoph

Das oberste Antwort war eine Umgehung eines Angular-Problems, das nicht mehr existiert (zumindest in Versionen> 1.2.16 und "wahrscheinlich früher"), wie @zumalifeguard erwähnt hat. Aber ich lese alle diese Antworten ohne eine wirkliche Lösung. 

Es scheint mir, dass die Antwort jetzt sein sollte 

  • verwenden Sie $broadcast aus dem $rootScope
  • hören Sie mit $onvom lokalen $scope, der über das Ereignis Bescheid wissen muss

Also zu veröffentlichen

// EXAMPLE PUBLISHER
angular.module('test').controller('CtrlPublish', ['$rootScope', '$scope',
function ($rootScope, $scope) {

  $rootScope.$broadcast('topic', 'message');

}]);

Und abonnieren

// EXAMPLE SUBSCRIBER
angular.module('test').controller('ctrlSubscribe', ['$scope',
function ($scope) {

  $scope.$on('topic', function (event, arg) { 
    $scope.receiver = 'got your ' + arg;
  });

}]);

Plunker

Wenn Sie den Listener im lokalen $scope registrieren, wird er wird automatisch von $destroy selbst zerstört } gelöscht, wenn der zugehörige Controller entfernt wird. 

103
poshest

Verwenden Sie $ rootScope. $ Broadcast und $ scope. $ On für eine PubSub-Kommunikation.

Siehe auch diesen Beitrag: AngularJS - Kommunikation zwischen Controllern

53

Da defineProperty ein Problem mit der Browserkompatibilität aufweist, können wir über die Verwendung eines Dienstes nachdenken.

angular.module('myservice', [], function($provide) {
    $provide.factory('msgBus', ['$rootScope', function($rootScope) {
        var msgBus = {};
        msgBus.emitMsg = function(msg) {
        $rootScope.$emit(msg);
        };
        msgBus.onMsg = function(msg, scope, func) {
            var unbind = $rootScope.$on(msg, func);
            scope.$on('$destroy', unbind);
        };
        return msgBus;
    }]);
});

und verwende es wie folgt im Controller:

  • controller 1 

    function($scope, msgBus) {
        $scope.sendmsg = function() {
            msgBus.emitMsg('somemsg')
        }
    }
    
  • controller 2 

    function($scope, msgBus) {
        msgBus.onMsg('somemsg', $scope, function() {
            // your logic
        });
    }
    
42
Singo

GridLinked hat eine PubSub -Lösung veröffentlicht, die ziemlich gut zu sein scheint. Der Dienst ist hier zu finden.

Auch ein Diagramm ihres Dienstes:

Messaging Service

20
Ryan Schumacher

Die Verwendung von Emit und Broadcast ist eigentlich ineffizient, da das Ereignis in der Hierarchie des Bereichs nach oben und unten sprudelt, was bei einer komplexen Anwendung leicht zu einer Leistungsabsenkung führen kann.

Ich würde vorschlagen, einen Dienst zu nutzen. So habe ich es kürzlich in einem meiner Projekte implementiert - https://Gist.github.com/3384419

Grundidee - Registrieren Sie einen Pubsub/Event-Bus als Dienst. Fügen Sie diesen Eventbus dann an der Stelle ein, an der Sie Ereignisse/Themen abonnieren oder veröffentlichen müssen.

15
numan salati

Wenn Sie innerhalb eines Dienstes get- und set-Methoden verwenden, können Sie Nachrichten sehr einfach zwischen Steuerungen weiterleiten.

var myApp = angular.module("myApp",[]);

myApp.factory('myFactoryService',function(){


    var data="";

    return{
        setData:function(str){
            data = str;
        },

        getData:function(){
            return data;
        }
    }


})


myApp.controller('FirstController',function($scope,myFactoryService){
    myFactoryService.setData("Im am set in first controller");
});



myApp.controller('SecondController',function($scope,myFactoryService){
    $scope.rslt = myFactoryService.getData();
});

in HTML HTML können Sie dies überprüfen

<div ng-controller='FirstController'>  
</div>

<div ng-controller='SecondController'>
    {{rslt}}
</div>
14
Load Reconn

Bezüglich des ursprünglichen Codes - es scheint, dass Sie Daten zwischen Bereichen austauschen möchten. Um Daten oder Status zwischen dem $ -Zonenbereich freizugeben, schlagen die Dokumente die Verwendung eines Dienstes vor:

  • So führen Sie zustandslosen oder zustandsbehafteten Code aus, der von Controllern gemeinsam verwendet wird: Verwenden Sie stattdessen die Angle-Dienste. 
  • Zum Instanziieren oder Verwalten des Lebenszyklus anderer Komponenten (z. B. zum Erstellen von Service-Instanzen).

Ref: Angular Docs Link hier

8
pkbyron

Ich habe tatsächlich angefangen, Postal.js als Nachrichtenbus zwischen Controllern zu verwenden.

Der Message-Bus bietet viele Vorteile, wie z. B. AMQP-Style-Bindungen, die Integration von Post mit iFrames und Web-Sockets und vieles mehr.

Ich habe einen Dekorateur verwendet, um Postal auf $scope.$bus ... einzurichten.

angular.module('MyApp')  
.config(function ($provide) {
    $provide.decorator('$rootScope', ['$delegate', function ($delegate) {
        Object.defineProperty($delegate.constructor.prototype, '$bus', {
            get: function() {
                var self = this;

                return {
                    subscribe: function() {
                        var sub = postal.subscribe.apply(postal, arguments);

                        self.$on('$destroy',
                        function() {
                            sub.unsubscribe();
                        });
                    },
                    channel: postal.channel,
                    publish: postal.publish
                };
            },
            enumerable: false
        });

        return $delegate;
    }]);
});

Hier ist ein Link zu einem Blogbeitrag zum Thema ...
http://jonathancreamer.com/an-angular-event-bus-with-postal-js/

5
jcreamer898

So mache ich das mit Factory/Services und Simple Dependency Injection (DI) .

myApp = angular.module('myApp', [])

# PeopleService holds the "data".
angular.module('myApp').factory 'PeopleService', ()->
  [
    {name: "Jack"}
  ]

# Controller where PeopleService is injected
angular.module('myApp').controller 'PersonFormCtrl', ['$scope','PeopleService', ($scope, PeopleService)->
  $scope.people = PeopleService
  $scope.person = {} 

  $scope.add = (person)->
    # Simply Push some data to service
    PeopleService.Push angular.copy(person)
]

# ... and again consume it in another controller somewhere...
angular.module('myApp').controller 'PeopleListCtrl', ['$scope','PeopleService', ($scope, PeopleService)->
  $scope.people = PeopleService
]
3
Oto Brglez

Ich mochte die Art und Weise, wie $rootscope.emit für die Kommunikation untereinander verwendet wurde. Ich empfehle die saubere und leistungsfähige Lösung, ohne den Weltraum zu verschmutzen.

module.factory("eventBus",function (){
    var obj = {};
    obj.handlers = {};
    obj.registerEvent = function (eventName,handler){
        if(typeof this.handlers[eventName] == 'undefined'){
        this.handlers[eventName] = [];  
    }       
    this.handlers[eventName].Push(handler);
    }
    obj.fireEvent = function (eventName,objData){
       if(this.handlers[eventName]){
           for(var i=0;i<this.handlers[eventName].length;i++){
                this.handlers[eventName][i](objData);
           }

       }
    }
    return obj;
})

//Usage:

//In controller 1 write:
eventBus.registerEvent('fakeEvent',handler)
function handler(data){
      alert(data);
}

//In controller 2 write:
eventBus.fireEvent('fakeEvent','fakeData');
3
shikhar chauhan

Hier ist der schnelle und schmutzige Weg.

// Add $injector as a parameter for your controller

function myAngularController($scope,$injector){

    $scope.sendorders = function(){

       // now you can use $injector to get the 
       // handle of $rootScope and broadcast to all

       $injector.get('$rootScope').$broadcast('sinkallships');

    };

}

Hier ist eine Beispielfunktion, die Sie in einem beliebigen Controller hinzufügen können

$scope.$on('sinkallships', function() {

    alert('Sink that ship!');                       

});

und hier ist natürlich dein HTML:

<button ngclick="sendorders()">Sink Enemy Ships</button>
2
Peter Drinnan

Auf diese Hallo-Funktion können Sie überall im Modul zugreifen 

Controller eins

 $scope.save = function() {
    $scope.hello();
  }

zweiter Controller

  $rootScope.hello = function() {
    console.log('hello');
  }

Mehr Infos hier

0
Prashobh

Beginnend mit Winkel 1.5 und dem komponentenbasierten Entwicklungsfokus. Die empfohlene Methode für die Interaktion von Komponenten ist die Verwendung der Eigenschaft 'Request' und der Eigenschaftsbindung (Eingabe/Ausgabe).

Eine Komponente würde eine andere Komponente (zum Beispiel die Wurzelkomponente) erfordern und einen Verweis auf ihren Controller erhalten:

angular.module('app').component('book', {
    bindings: {},
    require: {api: '^app'},
    template: 'Product page of the book: ES6 - The Essentials',
    controller: controller
});

Sie können dann die Methoden der Stammkomponente in Ihrer untergeordneten Komponente verwenden:

$ctrl.api.addWatchedBook('ES6 - The Essentials');

Dies ist die Hauptkomponenten-Controller-Funktion:

function addWatchedBook(bookName){

  booksWatched.Push(bookName);

}

Hier ist eine vollständige architektonische Übersicht: Komponentenkommunikation

0
kevinius

Sie können den eingebauten AngularJS-Dienst $rootScope verwenden und diesen Dienst in Ihre beiden Controller einbinden. __ Sie können dann Ereignisse überwachen, die für das $ rootScope-Objekt ausgelöst werden. 

$ rootScope bietet zwei Ereignis-Dispatcher mit dem Namen $emit and $broadcast, die für das Auslösen von Ereignissen verantwortlich sind (möglicherweise benutzerdefinierte Ereignisse), und verwenden die Funktion $rootScope.$on, um den Ereignislistener hinzuzufügen.

0
Shivang Gupta

Sie können dies tun, indem Sie winklige Ereignisse verwenden, die $ emit und $ broadcast sind. Nach unserem Wissen ist dies der beste, effizienteste und effektivste Weg.

Zuerst rufen wir eine Funktion von einem Controller aus auf.

var myApp = angular.module('sample', []);
myApp.controller('firstCtrl', function($scope) {
    $scope.sum = function() {
        $scope.$emit('sumTwoNumber', [1, 2]);
    };
});
myApp.controller('secondCtrl', function($scope) {
    $scope.$on('sumTwoNumber', function(e, data) {
        var sum = 0;
        for (var a = 0; a < data.length; a++) {
            sum = sum + data[a];
        }
        console.log('event working', sum);

    });
});

Sie können $ rootScope auch anstelle von $ scope verwenden. Verwenden Sie Ihren Controller entsprechend.

0
Peeyush Kumar
function mySrvc() {
  var callback = function() {

  }
  return {
    onSaveClick: function(fn) {
      callback = fn;
    },
    fireSaveClick: function(data) {
      callback(data);
    }
  }
}

function controllerA($scope, mySrvc) {
  mySrvc.onSaveClick(function(data) {
    console.log(data)
  })
}

function controllerB($scope, mySrvc) {
  mySrvc.fireSaveClick(data);
}
0
Amin Rahimi

Sie sollten den Dienst verwenden, da $rootscope Zugriff von der gesamten Anwendung aus ist und die Auslastung erhöht, oder Sie können die rootparams verwenden, wenn Ihre Daten nicht mehr sind.

0
abhaygarg12493

Ich werde einen Dienst erstellen und eine Benachrichtigung verwenden.

  1. Erstellen Sie eine Methode im Benachrichtigungsdienst
  2. Erstellen Sie eine generische Methode zum Senden von Benachrichtigungen in Notification Service.
  3. Rufen Sie vom Quellcontroller die notificationService.Method auf. Ich übergebe auch das entsprechende Objekt, um bei Bedarf zu bleiben.
  4. Innerhalb der Methode behalte ich Daten im Benachrichtigungsdienst und rufe die generische Benachrichtigungsmethode auf.
  5. Im Ziel-Controller höre ich ($ scope.on) auf das Broadcast-Ereignis und greife auf Daten vom Benachrichtigungsdienst zu. 

Da der Notification Service zu jedem Zeitpunkt Singleton ist, sollte er in der Lage sein, persistente Daten bereitzustellen.

Hoffe das hilft

0
rahul