AngularJS: Como posso passar variables ​​entre controladores?

Eu tenho dois controladores angulares:

function Ctrl1($scope) { $scope.prop1 = "First"; } function Ctrl2($scope) { $scope.prop2 = "Second"; $scope.both = Ctrl1.prop1 + $scope.prop2; //This is what I would like to do ideally } 

Eu não posso usar Ctrl1 dentro de Ctrl2 porque é indefinido. No entanto, se eu tentar passar isso assim …

 function Ctrl2($scope, Ctrl1) { $scope.prop2 = "Second"; $scope.both = Ctrl1.prop1 + $scope.prop2; //This is what I would like to do ideally } 

Eu recebo um erro. Alguém sabe como fazer isso?

Fazendo

 Ctrl2.prototype = new Ctrl1(); 

Também falha.

NOTA: Esses controladores não estão nesteds uns dentro dos outros.

Uma maneira de compartilhar variables ​​entre vários controladores é criar um serviço e injetá-lo em qualquer controlador onde você queira usá-lo.

Exemplo de serviço simples:

 angular.module('myApp', []) .service('sharedProperties', function () { var property = 'First'; return { getProperty: function () { return property; }, setProperty: function(value) { property = value; } }; }); 

Usando o serviço em um controlador:

 function Ctrl2($scope, sharedProperties) { $scope.prop2 = "Second"; $scope.both = sharedProperties.getProperty() + $scope.prop2; } 

Isso é descrito muito bem neste blog (Lição 2 e em particular).

Descobri que, se você deseja vincular-se a essas propriedades em vários controladores, ele funciona melhor se você vincular a propriedade de um object em vez de um tipo primitivo (booleano, string, número) para manter a referência vinculada.

Exemplo: var property = { Property1: 'First' }; em vez de var property = 'First'; .


UPDATE: Para (espero) deixar as coisas mais claras aqui é um violino que mostra um exemplo de:

  • Ligação a cópias estáticas do valor compartilhado (em myController1)
    • Ligação a um primitivo (string)
    • Ligação à propriedade de um object (salva em uma variável de escopo)
  • Ligação a valores compartilhados que atualizam a interface do usuário conforme os valores são atualizados (em myController2)
    • Ligação a uma function que retorna uma primitiva (string)
    • Vinculando à propriedade do object
    • Vinculação bidirecional à propriedade de um object

Eu gosto de ilustrar coisas simples por exemplos simples 🙂

Aqui está um exemplo de Service muito simples:

 angular.module('toDo',[]) .service('dataService', function() { // private variable var _dataObj = {}; // public API this.dataObj = _dataObj; }) .controller('One', function($scope, dataService) { $scope.data = dataService.dataObj; }) .controller('Two', function($scope, dataService) { $scope.data = dataService.dataObj; }); 

E aqui o jsbin

E aqui está um exemplo muito simples de Factory :

 angular.module('toDo',[]) .factory('dataService', function() { // private variable var _dataObj = {}; // public API return { dataObj: _dataObj }; }) .controller('One', function($scope, dataService) { $scope.data = dataService.dataObj; }) .controller('Two', function($scope, dataService) { $scope.data = dataService.dataObj; }); 

E aqui o jsbin


Se isso é muito simples, aqui está um exemplo mais sofisticado

Veja também a resposta aqui para comentários relacionados às melhores práticas

— Eu sei que esta resposta não é para esta pergunta, mas eu quero que as pessoas que lêem esta questão e queiram lidar com Serviços como Fábricas para evitar problemas fazendo isso —-

Para isso, você precisará usar um serviço ou uma fábrica.

Os serviços são a MELHOR PRÁTICA para compartilhar dados entre controladores não nesteds.

Uma anotação muito boa sobre esse tópico sobre compartilhamento de dados é como declarar objects. Tive azar porque caí em uma armadilha do AngularJS antes de ler sobre isso e fiquei muito frustrada. Então deixe-me ajudá-lo a evitar esse problema.

Eu li no “ng-book: O livro completo sobre AngularJS” que os modelos AngularJS que são criados nos controladores como dados nus são ERRADOS!

Um elemento de escopo $ deve ser criado assim:

 angular.module('myApp', []) .controller('SomeCtrl', function($scope) { // best practice, always use a model $scope.someModel = { someValue: 'hello computer' }); 

E não assim:

 angular.module('myApp', []) .controller('SomeCtrl', function($scope) { // anti-pattern, bare value $scope.someBareValue = 'hello computer'; }; }); 

Isto é porque é recomendado (MELHOR PRÁTICA) para o DOM (documento html) para conter as chamadas como

 
//NOTICE THE DOT.

Isso é muito útil para controladores nesteds se você quiser que seu controlador filho possa alterar um object do controlador pai ….

Mas, no seu caso, você não quer escopos nesteds, mas há um aspecto semelhante para obter objects de serviços para os controladores.

Vamos dizer que você tem seu serviço ‘Factory’ e no espaço de retorno há um object A que contém o object B que contém o object C.

Se a partir do seu controlador você deseja obter o object C em seu escopo, é um erro dizer:

 $scope.neededObjectInController = Factory.objectA.objectB.objectC; 

Isso não vai funcionar … Em vez disso, use apenas um ponto.

 $scope.neededObjectInController = Factory.ObjectA; 

Então, no DOM você pode chamar objectC from objectA. Essa é uma prática recomendada relacionada a fábricas e, mais importante, ajudará a evitar erros inesperados e não alcançáveis.

Solução sem criar serviço, usando $ rootScope:

Para compartilhar propriedades nos Controladores de aplicativos, você pode usar o Angular $ rootScope. Essa é outra opção para compartilhar dados, colocando-os para que as pessoas saibam disso.

A maneira preferida de compartilhar algumas funcionalidades em Controllers é Services, para ler ou alterar uma propriedade global que você pode usar $ rootscope.

 var app = angular.module('mymodule',[]); app.controller('Ctrl1', ['$scope','$rootScope', function($scope, $rootScope) { $rootScope.showBanner = true; }]); app.controller('Ctrl2', ['$scope','$rootScope', function($scope, $rootScope) { $rootScope.showBanner = false; }]); 

Usando $ rootScope em um template (Acesse propriedades com $ root):

 

A amostra acima funcionou como um encanto. Acabei de fazer uma modificação apenas no caso de precisar gerenciar vários valores. Eu espero que isso ajude!

 app.service('sharedProperties', function () { var hashtable = {}; return { setValue: function (key, value) { hashtable[key] = value; }, getValue: function (key) { return hashtable[key]; } } }); 

Eu costumo usar valores, feliz por qualquer um para discutir por que isso é uma má idéia ..

 var myApp = angular.module('myApp', []); myApp.value('sharedProperties', {}); //set to empty object - 

Em seguida, injete o valor conforme um serviço.

Defina em ctrl1:

 myApp.controller('ctrl1', function DemoController(sharedProperties) { sharedProperties.carModel = "Galaxy"; sharedProperties.carMake = "Ford"; }); 

e access de ctrl2:

 myApp.controller('ctrl2', function DemoController(sharedProperties) { this.car = sharedProperties.carModel + sharedProperties.carMake; }); 

Eu gostaria de contribuir com essa questão apontando que a maneira recomendada de compartilhar dados entre controladores e até diretivas é usando serviços (fábricas) como já foi apontado, mas também gostaria de fornecer uma exemplo prático prático de como isso deve ser feito.

Aqui está o plunker de trabalho: http://plnkr.co/edit/Q1VdKJP2tpvqqJL1LF6m?p=info

Primeiro, crie seu serviço , que terá seus dados compartilhados :

 app.factory('SharedService', function() { return { sharedObject: { value: '', value2: '' } }; }); 

Em seguida, basta injetá-lo em seus controladores e pegar os dados compartilhados em seu escopo:

 app.controller('FirstCtrl', function($scope, SharedService) { $scope.model = SharedService.sharedObject; }); app.controller('SecondCtrl', function($scope, SharedService) { $scope.model = SharedService.sharedObject; }); app.controller('MainCtrl', function($scope, SharedService) { $scope.model = SharedService.sharedObject; }); 

Você também pode fazer isso por suas diretivas , funciona da mesma maneira:

 app.directive('myDirective',['SharedService', function(SharedService){ return{ restrict: 'E', link: function(scope){ scope.model = SharedService.sharedObject; }, template: '
' } }]);

Espero que esta resposta prática e limpa possa ser útil para alguém.

O exemplo a seguir mostra como passar variables ​​entre os controladores irmãos e executar uma ação quando o valor é alterado.

Exemplo de caso de uso: você tem um filtro em uma barra lateral que altera o conteúdo de outra visão.

 angular.module('myApp', []) .factory('MyService', function() { // private var value = 0; // public return { getValue: function() { return value; }, setValue: function(val) { value = val; } }; }) .controller('Ctrl1', function($scope, $rootScope, MyService) { $scope.update = function() { MyService.setValue($scope.value); $rootScope.$broadcast('increment-value-event'); }; }) .controller('Ctrl2', function($scope, MyService) { $scope.value = MyService.getValue(); $scope.$on('increment-value-event', function() { $scope.value = MyService.getValue(); }); }); 
  

Controller 1 Scope


Controller 2 Scope

Value: {{ value }}

Você não poderia também tornar a propriedade parte do pai do escopo?

 $scope.$parent.property = somevalue; 

Eu não estou dizendo que é certo, mas funciona.

Ah, tenha um pouco desse novo material como outra alternativa. É localstorage e funciona onde obras angulares. Seja bem-vindo. (Mas realmente, agradeço ao cara)

https://github.com/gsklee/ngStorage

Defina seus padrões:

 $scope.$storage = $localStorage.$default({ prop1: 'First', prop2: 'Second' }); 

Acesse os valores:

 $scope.prop1 = $localStorage.prop1; $scope.prop2 = $localStorage.prop2; 

Armazene os valores

 $localStorage.prop1 = $scope.prop1; $localStorage.prop2 = $scope.prop2; 

Lembre-se de injetar ngStorage no seu aplicativo e $ localStorage no seu controlador.

Você poderia fazer isso com serviços ou fábricas. Eles são essencialmente os mesmos separados para algumas diferenças fundamentais. Eu encontrei esta explicação no thinkster.io para ser o mais fácil de seguir. Simples, ao ponto e eficaz.

Existem duas maneiras de fazer isso

1) Use o serviço get / set

2) $scope.$emit('key', {data: value}); //to set the value $scope.$emit('key', {data: value}); //to set the value

  $rootScope.$on('key', function (event, data) {}); // to get the value 

Segunda abordagem:

 angular.module('myApp', []) .controller('Ctrl1', ['$scope', function($scope) { $scope.prop1 = "First"; $scope.clickFunction = function() { $scope.$broadcast('update_Ctrl2_controller', $scope.prop1); }; } ]) .controller('Ctrl2', ['$scope', function($scope) { $scope.prop2 = "Second"; $scope.$on("update_Ctrl2_controller", function(event, prop) { $scope.prop = prop; $scope.both = prop + $scope.prop2; }); } ]) 

Html:

 

{{both}}

Para mais detalhes veja plunker:

http://plnkr.co/edit/cKVsPcfs1A1Wwlud2jtO?p=preview

Se você não quiser fazer o serviço, então você pode fazer assim.

 var scope = angular.element("#another ctrl scope element id.").scope(); scope.plean_assign = some_value; 

Além de $ rootScope e serviços, há uma solução alternativa limpa e fácil para estender o ângulo para adicionar os dados compartilhados:

nos controladores:

 angular.sharedProperties = angular.sharedProperties || angular.extend(the-properties-objects); 

Essas propriedades pertencem ao object ‘angular’, separadas dos escopos e podem ser compartilhadas em escopos e serviços.

Um benefício disso é que você não precisa injetar o object: eles podem ser acessados ​​em qualquer lugar imediatamente após a sua definição!