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.