Variáveis ​​globais no AngularJS

348

Eu tenho um problema em que estou inicializando uma variável no escopo em um controlador. Em seguida, ele é alterado em outro controlador quando um usuário efetua login. Essa variável é usada para controlar itens como a barra de navegação e restringe o acesso a partes do site, dependendo do tipo de usuário, por isso é importante que ele mantenha seu valor. O problema é que o controlador que o inicializa, é chamado novamente de alguma forma angular e, em seguida, redefine a variável de volta ao seu valor inicial.

Eu suponho que essa não é a maneira correta de declarar e inicializar variáveis ​​globais, bem, não é realmente global, então minha pergunta é qual é a maneira correta e existem bons exemplos em torno desse trabalho com a versão atual do angular?

Lâmpada1
fonte
12
Como esse é o resultado número 1 do Google: agora você pode usar app.constant () e app.value () para criar constantes e variáveis ​​em todo o aplicativo. Mais aqui: bit.ly/1P51PED
Mroz

Respostas:

491

Você tem basicamente 2 opções para variáveis ​​"globais":

$rootScopeé pai de todos os escopos; portanto, os valores expostos serão visíveis em todos os modelos e controladores. Usar o $rootScopeé muito fácil, pois você pode simplesmente injetá-lo em qualquer controlador e alterar valores nesse escopo. Pode ser conveniente, mas tem todos os problemas de variáveis ​​globais .

Os serviços são singletons que você pode injetar em qualquer controlador e expor seus valores no escopo de um controlador. Serviços, sendo singletons ainda são 'globais', mas você tem um controle muito melhor sobre onde eles são usados ​​e expostos.

O uso de serviços é um pouco mais complexo, mas não tanto, aqui está um exemplo:

var myApp = angular.module('myApp',[]);
myApp.factory('UserService', function() {
  return {
      name : 'anonymous'
  };
});

e depois em um controlador:

function MyCtrl($scope, UserService) {
    $scope.name = UserService.name;
}

Aqui está o jsFiddle em funcionamento: http://jsfiddle.net/pkozlowski_opensource/BRWPM/2/

pkozlowski.opensource
fonte
141
Do FAQ angular : Por outro lado, não crie um serviço cujo único objetivo na vida seja armazenar e retornar bits de dados.
Jakob Stoeck
7
Estou usando a semente Express + Angular da Btford . Se eu definir uma variável no $ rootScope no Controller1 say e mudar para outro URL (desenvolvido pelo Controller2 say), eu posso acessar essa variável. No entanto, se eu atualizar a página no Controller2, a variável não estará mais acessível no $ rootScope. Se estou salvando dados do usuário ao entrar, como posso garantir que esses dados estejam acessíveis em outro lugar, mesmo na atualização da página?
Craig Myles
19
@JakobStoeck Combine isso com esta resposta e você ficará colocando os dados no rootScope. Também não acho isso certo. Qual é a maneira angular de armazenar e retornar bits de dados usados ​​globalmente?
user2483724
11
Estou curioso para saber quais são as desvantagens de um serviço específico para armazenar valores. Isolado, injetado apenas onde você precisar e facilmente testável. Qual é a desvantagem?
Brian
12
Oh, Deus, por que todo mundo tem medo de variáveis globais REAAL, se você sabe o que a sua aplicação será composto de, basta fazer uma verdadeira variável js globais em vez complicar coisas
Max Yari
93

Se você deseja apenas armazenar um valor, de acordo com a documentação Angular sobre provedores , use a receita Valor:

var myApp = angular.module('myApp', []);
myApp.value('clientId', 'a12345654321x');

Em seguida, use-o em um controlador como este:

myApp.controller('DemoController', ['clientId', function DemoController(clientId) {
    this.clientId = clientId;
}]);

O mesmo pode ser alcançado usando um provedor, fábrica ou serviço, pois eles são "apenas açúcar sintático em cima de uma receita de provedor", mas o uso do Value alcançará o que você deseja com uma sintaxe mínima.

A outra opção é usar $rootScope, mas não é realmente uma opção, porque você não deve usá-la pelos mesmos motivos que não deve usar variáveis ​​globais em outros idiomas. É aconselhável que seja usado com moderação.

Como todos os escopos são herdados $rootScope, se você tiver uma variável $rootScope.datae alguém esquecer que datajá está definido e cria $scope.dataem um escopo local, você terá problemas.


Se você deseja modificar esse valor e persistir em todos os seus controladores, use um objeto e modifique as propriedades, lembrando que o Javascript é passado por "cópia de uma referência" :

myApp.value('clientId', { value: 'a12345654321x' });
myApp.controller('DemoController', ['clientId', function DemoController(clientId) {
    this.clientId = clientId;
    this.change = function(value) {
        clientId.value = 'something else';
    }
}];

Exemplo JSFiddle

Dean Or
fonte
11
Quando alterei o valor em uma visualização, o novo valor não é persistente. Por quê? Você pode atualizar sua resposta e vamos ver como você clientIdpode ser atualizada?
Blaise
@DeanOr É possível preservar o valor atualizado entre as atualizações da página? O valor será reinicializado se eu atualizar a página.
Nabarun 19/07
Você precisará usar outra coisa para isso, como um cookie, armazenamento local ou banco de dados.
Dean Ou
11
Eu realmente gosto deste melhor. Agora eu posso modificar pelo processo de compilação de dev, palco, e prod
jemiloii
11
Há um artigo simples explicando o uso de constantes e valores semelhantes às variáveis globais: ilikekillnerds.com/2014/11/...
RushabhG
36

Exemplo de "variáveis ​​globais" do AngularJS usando $rootScope:

O controlador 1 define a variável global:

function MyCtrl1($scope, $rootScope) {
    $rootScope.name = 'anonymous'; 
}

O controlador 2 lê a variável global:

function MyCtrl2($scope, $rootScope) {
    $scope.name2 = $rootScope.name; 
}

Aqui está um jsFiddle em funcionamento: http://jsfiddle.net/natefriedman/3XT3F/1/

NateFriedman
fonte
2
Isso não funciona em exibições de diretivas de escopo isoladas. Consulte jsfiddle.net/3XT3F/7 . Mas você pode usar $ root para contornar isso. Veja jsfiddle.net/3XT3F/10 . Obrigado a @natefaubion por apontar isso
Tony Lâmpada 20/09/2013
2
na atualização, o valor $ rootScope estaria vazio.
xyonme
codificar o $ rootScope.name é igual a algum valor ... na verdade, precisamos lê-lo e configurá-lo lá.
25

No interesse de adicionar outra idéia ao pool do wiki, mas e o AngularJS valuee os constantmódulos? Só estou começando a usá-los, mas parece-me que essas são provavelmente as melhores opções aqui.

Nota: até o momento em que este artigo foi escrito, o Angular 1.3.7 é o último estábulo, acredito que eles foram adicionados no 1.2.0, mas não o confirmaram com o changelog.

Dependendo de quantos você precisa definir, convém criar um arquivo separado para eles. Mas geralmente os defino antes do .config()bloqueio do meu aplicativo para facilitar o acesso. Como ainda são módulos efetivamente, você precisará confiar na injeção de dependência para usá-los, mas eles são considerados "globais" para o seu módulo de aplicativo.

Por exemplo:

angular.module('myApp', [])
  .value('debug', true)
  .constant('ENVIRONMENT', 'development')
  .config({...})

Depois, dentro de qualquer controlador:

angular.module('myApp')
  .controller('MainCtrl', function(debug, ENVIRONMENT), {
    // here you can access `debug` and `ENVIRONMENT` as straight variables
  })

A partir da pergunta inicial, na verdade, parece que as propriedades estáticas são necessárias aqui de qualquer maneira, seja mutável (valor) ou final (constante). É mais minha opinião pessoal do que qualquer outra coisa, mas acho que colocar itens de configuração de tempo de execução no $rootScopefica muito confuso, muito rapidamente.


fonte
Acho o módulo de valor muito útil e conciso. especialmente quando você ligá-la a uma variável $ escopo dentro de um controlador, de modo que as mudanças dentro da lógica ou exibição que do controlador está ligado à variável / valor / serviço global
Ben Wheeler
20
// app.js or break it up into seperate files
// whatever structure is your flavor    
angular.module('myApp', [])    

.constant('CONFIG', {
    'APP_NAME' : 'My Awesome App',
    'APP_VERSION' : '0.0.0',
    'GOOGLE_ANALYTICS_ID' : '',
    'BASE_URL' : '',
    'SYSTEM_LANGUAGE' : ''
})

.controller('GlobalVarController', ['$scope', 'CONFIG', function($scope, CONFIG) {

    // If you wish to show the CONFIG vars in the console:
    console.log(CONFIG);

    // And your CONFIG vars in .constant will be passed to the HTML doc with this:
    $scope.config = CONFIG;
}]);

No seu HTML:

<span ng-controller="GlobalVarController">{{config.APP_NAME}} | v{{config.APP_VERSION}}</span>

fonte
8
localStorage.username = 'blah'

Se você tem a garantia de estar em um navegador moderno. Embora saiba que seus valores serão todos transformados em strings.

Também tem o benefício útil de ser armazenado em cache entre recarregamentos.

Kevin
fonte
5
Heh, vou armazenar todas as variáveis ​​via localStorage. Até teremos algum tipo de persistência! Além disso, para contornar a limitação de string, vamos armazenar o .toString () de uma função e avaliar quando necessário!
Shaz
como já mencionado por @Shaz, este tem o problema da persistência
Żubrówka
7

Corrija-me se estiver errado, mas quando o Angular 2.0 for lançado, não acredito que $rootScopeexistirá. Minha conjectura é baseada no fato de que também $scopeestá sendo removida. Obviamente, os controladores ainda existirão, mas não da ng-controllermoda. Pense em injetar controladores nas diretivas. Como o lançamento é iminente, será melhor usar os serviços como variáveis ​​globais, se você quiser um tempo mais fácil para mudar da versão 1.X para a versão 2.0.

jason328
fonte
Concordo que colocar dados diretamente no $ rootScope é contra todo o propósito da Angular. Tome um pouco de tempo e organize seu código corretamente, e isso ajudará você, não apenas na atualização do AngularJS 2.x, mas geralmente em todo o desenvolvimento do aplicativo, à medida que se torna mais complexo.
CatalinBerta
3
Se $ rootScope é removido, basta escrever um novo serviço que é chamado de "$ rootScope" :)
uylmz
3

Você também pode usar a variável de ambiente $windowpara que uma variável global declarada fora de um controlador possa ser verificada dentro de um$watch

var initWatch = function($scope,$window){
    $scope.$watch(function(scope) { return $window.globalVar },
        function(newValue) {
            $scope.updateDisplayedVar(newValue);
    });
}

Por certo, o ciclo de resumo é mais longo com esses valores globais, portanto nem sempre é atualizado em tempo real. Eu preciso investigar esse tempo de digestão com esta configuração.

Megaman
fonte
0

Acabei de encontrar outro método por engano:

O que eu fiz foi declarar uma var db = nulldeclaração de aplicativo acima e modificá-la no app.jsmomento em que eu a acessei, e controller.js consegui acessá-la sem nenhum problema.Pode haver alguns problemas com esse método que eu não conheço, mas é uma boa solução, eu acho.

Mamba negra
fonte
0

Tente isso, você não forçará a injetar $rootScopeno controlador.

app.run(function($rootScope) {
    $rootScope.Currency = 'USD';
});

Você pode usá-lo apenas no bloco de execução, porque o bloco de configuração não fornecerá o serviço do $ rootScope.

Rahul Murari
fonte
0

Na verdade, é bem fácil. (Se você estiver usando o Angular 2+ de qualquer maneira.)

Basta adicionar

declare var myGlobalVarName;

Em algum lugar na parte superior do arquivo do componente (como após as instruções "import"), você poderá acessar "myGlobalVarName" em qualquer lugar do componente.

Andy Corman
fonte
-2

Você também pode fazer algo assim ..

function MyCtrl1($scope) {
    $rootScope.$root.name = 'anonymous'; 
}

function MyCtrl2($scope) {
    var name = $rootScope.$root.name;
}
pkdkk
fonte
3
$ rootScope é indefinido quando você o usa dessa maneira.
Ahmad Ahmadi