varios servicios que entre directivas controladores comunicacion scope angularjs

scope - que - servicios en angular js



¿Cuál es la forma correcta de comunicarse entre los controladores en AngularJS? (19)

¿Cuál es la forma correcta de comunicarse entre los controladores?

Actualmente estoy usando un horrible fudge con 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); } }


Aquí está la manera rápida y sucia.

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

Aquí hay una función de ejemplo para agregar dentro de cualquiera de los controladores hermanos:

$scope.$on(''sinkallships'', function() { alert(''Sink that ship!''); });

y por supuesto aquí está su HTML:

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


Así es como lo hago con Factory / Services y inyección de dependencia simple (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 ]


Con los métodos de obtención y configuración dentro de un servicio, puede pasar mensajes entre los controladores de manera muy sencilla.

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

en HTML HTML puedes verificar como este

<div ng-controller=''FirstController''> </div> <div ng-controller=''SecondController''> {{rslt}} </div>


Crearé una notificación de servicio y uso.

  1. Crear un método en el servicio de notificaciones.
  2. Cree un método genérico para transmitir notificaciones en el Servicio de Notificación.
  3. Desde el controlador de origen llame al notificationService.Method. También paso el objeto correspondiente para persistir si es necesario.
  4. Dentro del método, conservo los datos en el servicio de notificación y llamo al método de notificación genérico.
  5. En el controlador de destino escucho ($ scope.on) el evento de transmisión y accedo a los datos del Servicio de Notificación.

Como en cualquier punto, el Servicio de Notificación es único, debe poder proporcionar datos persistentes en todo.

Espero que esto ayude


Dado que defineProperty tiene un problema de compatibilidad con el navegador, creo que podemos pensar en usar un servicio.

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

y usarlo en el controlador de esta manera:

  • controlador 1

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

  • controlador 2

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


De hecho, comencé a usar Postal.js como un bus de mensajes entre los controladores.

Hay muchos beneficios para él como un bus de mensajes como los enlaces de estilo AMQP, la forma en que postal puede integrarse con iFrames y sockets web, y muchas más cosas.

Usé un decorador para configurar Postal en $scope.$bus ...

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

Aquí hay un enlace a una entrada de blog sobre el tema ...
http://jonathancreamer.com/an-angular-event-bus-with-postal-js/


Debe usar el Servicio, ya que $rootscope es el acceso desde la aplicación completa, y aumenta la carga, o puede usar los rootparams si sus datos no son más.


En realidad, el uso de emitir y transmitir es ineficiente porque el evento aumenta y disminuye la jerarquía del alcance, lo que puede degradarse fácilmente en el rendimiento de una aplicación compleja.

Sugeriría utilizar un servicio. Aquí es cómo lo implementé recientemente en uno de mis proyectos: https://gist.github.com/3384419 .

Idea básica: registrar un pubsub / evento bus como un servicio. Luego inyecte ese bus de eventos donde sea que necesite suscribirse o publicar eventos / temas.


Iniciando angular 1.5 y su enfoque de desarrollo basado en componentes. La forma recomendada para que los componentes interactúen es mediante el uso de la propiedad ''require'' y mediante los enlaces de propiedad (entrada / salida).

Un componente requeriría otro componente (por ejemplo, el componente raíz) y obtener una referencia a su controlador:

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

Luego puede usar los métodos del componente raíz en su componente hijo:

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

Esta es la función del controlador de componente raíz:

function addWatchedBook(bookName){ booksWatched.push(bookName); }

Aquí está una descripción arquitectónica completa: Comunicaciones del componente


La respuesta principal aquí fue una solución a un problema Angular que ya no existe (al menos en las versiones> 1.2.16 y "probablemente antes") como lo mencionó @zumalifeguard. Pero me quedo leyendo todas estas respuestas sin una solución real.

Me parece que la respuesta ahora debería ser

  • usar $broadcast desde $rootScope
  • escuchar usando $on desde el $scope local $scope que necesita saber sobre el evento

Así que para publicar

// EXAMPLE PUBLISHER angular.module(''test'').controller(''CtrlPublish'', [''$rootScope'', ''$scope'', function ($rootScope, $scope) { $rootScope.$broadcast(''topic'', ''message''); }]);

Y suscribirse

// EXAMPLE SUBSCRIBER angular.module(''test'').controller(''ctrlSubscribe'', [''$scope'', function ($scope) { $scope.$on(''topic'', function (event, arg) { $scope.receiver = ''got your '' + arg; }); }]);

Plunkers

Si registra el oyente en el $scope local, $scope se destruirá automáticamente cuando se elimine el controlador asociado.


Me gustó la forma en que se utilizó $rootscope.emit para lograr la intercomunicación. Sugiero la solución limpia y eficaz en el rendimiento sin contaminar el espacio global.

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


Puede usar el servicio $rootScope e inyectar este servicio en sus dos controladores. Luego puede escuchar los eventos que se activan en el objeto $ rootScope.

$ rootScope proporciona dos despachadores de eventos llamados $emit and $broadcast que son responsables de enviar eventos (pueden ser eventos personalizados) y usan $rootScope.$on función para agregar un detector de eventos.


Puedes acceder a esta función de saludo en cualquier parte del módulo.

Controlador uno

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

segundo controlador

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

Más información aquí


Puedes hacerlo usando eventos angulares que son $ emit y $ broadcast. Según nuestro conocimiento, esta es la mejor manera, eficiente y eficaz.

Primero llamamos una función desde un controlador.

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

También puede usar $ rootScope en lugar de $ scope. Utilice su controlador en consecuencia.


Respecto al código original: parece que desea compartir datos entre ámbitos. Para compartir datos o estados entre $ alcance, los documentos sugieren usar un servicio:

  • Para ejecutar código sin estado o con estado compartido entre los controladores: use servicios angulares en su lugar.
  • Para crear una instancia o administrar el ciclo de vida de otros componentes (por ejemplo, para crear instancias de servicio).

Ref: Angular Docs link aquí



GridLinked publicó una solución de PubSub que parece estar diseñada bastante bien. El servicio se puede encontrar, here .

También un diagrama de su servicio:


Edición : el problema tratado en esta respuesta se resolvió en la versión 1.2.7 de angular.js. $broadcast ahora evita las burbujas en los ámbitos no registrados y se ejecuta tan rápido como $ emit.

Entonces, ahora puedes:

  • usar $broadcast desde $rootScope
  • escuchar usando $on desde el $scope local $scope que necesita saber sobre el evento

Respuesta original abajo

Recomiendo no usar $rootScope.$broadcast + $scope.$on $rootScope.$emit sino más bien $rootScope.$emit $rootScope.$on + $rootScope.$on . Lo primero puede causar serios problemas de rendimiento como lo plantea @numan. Eso es porque el evento se derrumbará a través de todos los ámbitos.

Sin embargo, este último (usando $rootScope.$emit + $rootScope.$on ) no sufre de esto y, por lo tanto, se puede usar como un canal de comunicación rápida.

De la documentación angular de $emit :

Envía un nombre de evento hacia arriba a través de la jerarquía de alcance notificando a los registrados

Dado que no hay un alcance por encima de $rootScope , no hay ningún $rootScope . Es totalmente seguro usar $rootScope.$emit() / $rootScope.$on() como EventBus.

Sin embargo, hay un gotcha cuando se usa desde los controladores. Si vincula directamente a $rootScope.$on() desde un controlador, deberá limpiar el enlace usted mismo cuando se destruya su $scope local. Esto se debe a que los controladores (a diferencia de los servicios) se pueden crear instancias varias veces durante la vida útil de una aplicación, lo que daría como resultado una combinación de enlaces que eventualmente crearía pérdidas de memoria en todo el lugar :)

Para cancelar el registro, simplemente escuche el evento $destroy $scope y luego llame a la función que devolvió $rootScope.$on .

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

Diría que eso no es realmente una cosa específica angular, ya que también se aplica a otras implementaciones de EventBus, que tiene que limpiar los recursos.

Sin embargo, puede hacer su vida más fácil para esos casos. Por ejemplo, podría $rootScope parche $rootScope y darle un $onRootScope que se suscriba a los eventos emitidos en $rootScope pero también limpia directamente el controlador cuando se destruye el $scope local.

La forma más limpia de parchear el $rootScope para proporcionar dicho método $onRootScope sería a través de un decorador (un bloque de ejecución probablemente también lo hará bien, pero pssst, no se lo digas a nadie)

Para asegurarnos de que la propiedad $onRootScope no se muestre inesperada al enumerar sobre $scope , usamos Object.defineProperty() y configuramos enumerable en false . Tenga en cuenta que es posible que necesite un calzo ES5.

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

Con este método en su lugar, el código del controlador de arriba se puede simplificar para:

angular .module(''MyApp'') .controller(''MyController'', [''$scope'', function MyController($scope) { $scope.$onRootScope(''someComponent.someCrazyEvent'', function(){ console.log(''foo''); }); } ]);

Entonces, como resultado final de todo esto, le recomiendo que use $rootScope.$emit + $scope.$onRootScope .

Por cierto, estoy tratando de convencer al equipo angular para que aborde el problema dentro del núcleo angular. Aquí hay una discusión: https://github.com/angular/angular.js/issues/4574

Aquí hay un jsperf que muestra cuánto del impacto de $broadcast llega a la mesa en un escenario decente con solo 100 $scope .

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


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