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?


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/


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


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/


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.


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>

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.


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.


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.


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.


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.


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.


-2

Você também pode fazer algo assim ..

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

function MyCtrl2($scope) {
    var name = $rootScope.$root.name;
}

3
$ rootScope é indefinido quando você o usa dessa maneira.
— Ahmad Ahmadi
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.