Variables globales dans AngularJS

348

J'ai un problème où j'initialise une variable sur la portée d'un contrôleur. Elle est ensuite modifiée dans un autre contrôleur lorsqu'un utilisateur se connecte. Cette variable est utilisée pour contrôler des éléments tels que la barre de navigation et restreint l'accès à certaines parties du site en fonction du type d'utilisateur, il est donc important qu'elle conserve sa valeur. Le problème avec cela est que le contrôleur qui l'initialise, est rappelé par un certain angulaire puis réinitialise la variable à sa valeur initiale.

Je suppose que ce n'est pas la bonne façon de déclarer et d'initialiser des variables globales, eh bien ce n'est pas vraiment global, donc ma question est quelle est la bonne façon et y a-t-il de bons exemples qui fonctionnent avec la version actuelle d'angular?

Ampoule1
la source
12
Étant donné qu'il s'agit du résultat n ° 1 de Google: vous pouvez désormais utiliser app.constant () et app.value () pour créer des constantes et des variables à l'échelle de l'application. Plus ici: bit.ly/1P51PED
Mroz

Réponses:

491

Vous avez essentiellement 2 options pour les variables "globales":

$rootScopeest un parent de toutes les étendues, les valeurs qui y sont exposées seront donc visibles dans tous les modèles et contrôleurs. L'utilisation de $rootScopeest très simple car vous pouvez simplement l'injecter dans n'importe quel contrôleur et modifier les valeurs dans cette étendue. Cela peut être pratique mais présente tous les problèmes des variables globales .

Les services sont des singletons que vous pouvez injecter à n'importe quel contrôleur et exposer leurs valeurs dans la portée d'un contrôleur. Les services, en tant que singletons, sont toujours «mondiaux» mais vous avez un bien meilleur contrôle sur leur utilisation et leur exposition.

L'utilisation des services est un peu plus complexe, mais pas tant que ça, voici un exemple:

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

puis dans un contrôleur:

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

Voici le jsFiddle qui fonctionne: http://jsfiddle.net/pkozlowski_opensource/BRWPM/2/

pkozlowski.opensource
la source
141
De la FAQ Angular : Inversement, ne créez pas un service dont le seul but dans la vie est de stocker et de renvoyer des bits de données.
Jakob Stoeck
7
J'utilise la graine Express + Angular de Btford . Si je mets une variable sur $ rootScope dans Controller1 et que je passe à une autre URL (alimentée par Controller2, par exemple), je peux accéder à cette variable. Cependant, si je rafraîchis la page sur Controller2, la variable n'est plus accessible sur $ rootScope. Si j'enregistre les données utilisateur lors de la connexion, comment puis-je m'assurer que ces données sont accessibles ailleurs, même lors de l'actualisation de la page?
Craig Myles
19
@JakobStoeck Combinez cela avec cette réponse et vous vous retrouvez avec la mise des données sur le rootScope. Je ne pense pas que ce soit juste non plus. Quelle est la manière angulaire de stocker et de renvoyer des bits de données utilisés à l'échelle mondiale?
user2483724
11
Je suis curieux de savoir quels sont les inconvénients d'un service spécifiquement destiné au stockage de valeurs. Isolé, injecté uniquement là où vous en avez besoin et facilement testable. Quel est l’inconvénient?
Brian
12
oh mon dieu, pourquoi tout le monde a peur des variables globales réelles, si vous savez de quoi votre application sera composée, créez simplement une variable js globale au lieu de trop compliquer les choses
Max Yari
93

Si vous souhaitez simplement stocker une valeur, selon la documentation Angular sur les fournisseurs , vous devez utiliser la recette Value:

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

Ensuite, utilisez-le dans un contrôleur comme celui-ci:

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

La même chose peut être obtenue en utilisant un fournisseur, une usine ou un service car ils ne sont «que du sucre syntaxique en plus d'une recette de fournisseur», mais en utilisant Value, vous obtiendrez ce que vous voulez avec une syntaxe minimale.

L'autre option est d'utiliser $rootScope, mais ce n'est pas vraiment une option parce que vous ne devriez pas l'utiliser pour les mêmes raisons que vous ne devriez pas utiliser de variables globales dans d'autres langues. Il est conseillé de l'utiliser avec parcimonie.

Étant donné que toutes les étendues héritent de $rootScope, si vous avez une variable $rootScope.dataet que quelqu'un oublie qui dataest déjà défini et crée $scope.datadans une étendue locale, vous rencontrerez des problèmes.


Si vous souhaitez modifier cette valeur et la faire persister sur tous vos contrôleurs, utilisez un objet et modifiez les propriétés en gardant à l'esprit que Javascript est passé par "copie d'une référence" :

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

Exemple JSFiddle

Dean Or
la source
1
Lorsque j'ai modifié la valeur sur une vue, la nouvelle valeur n'est pas persistante. Comment venir? Pouvez-vous mettre à jour votre réponse et laissez-nous voir comment la clientIdmettre à jour?
Blaise
@DeanOr Est-il possible de conserver la valeur mise à jour entre les rafraîchissements de la page? La valeur est réinitialisée si je rafraîchis la page.
Nabarun du
Vous devrez utiliser autre chose pour cela, comme un cookie, un stockage local ou une base de données.
Dean Or
1
J'aime vraiment celui-ci le meilleur. Je peux maintenant modifier par processus de construction pour dev, stage et prod
jemiloii
1
Il y a un article simple expliquant l'utilisation de constantes et de valeurs similaires aux variables globales: ilikekillnerds.com/2014/11/…
RushabhG
36

Exemple de "variables globales" AngularJS utilisant $rootScope:

Le contrôleur 1 définit la variable globale:

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

Le contrôleur 2 lit la variable globale:

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

Voici un jsFiddle fonctionnel: http://jsfiddle.net/natefriedman/3XT3F/1/

NateFriedman
la source
2
Cela ne fonctionne pas dans les vues de directives de portée isolées. Voir jsfiddle.net/3XT3F/7 . Mais vous pouvez utiliser $ root pour contourner ce problème. Voir jsfiddle.net/3XT3F/10 . Merci à @natefaubion de l' avoir signalé
Tony Lâmpada
2
lors de l'actualisation, la valeur $ rootScope serait vide.
xyonme
coder en dur le $ rootScope.name équivaut à une certaine valeur .. en fait, nous devons le lire et le placer là.
25

Dans l'intérêt d'ajouter une autre idée au pool wiki, mais qu'en est-il d'AngularJS valueet des constantmodules? Je commence à peine à les utiliser moi-même, mais il me semble que ce sont probablement les meilleures options ici.

Remarque: au moment de la rédaction, Angular 1.3.7 est la dernière version stable, je pense que ceux-ci ont été ajoutés dans 1.2.0, mais ne l'ont pas confirmé avec le journal des modifications.

Selon le nombre à définir, vous souhaiterez peut-être créer un fichier distinct pour eux. Mais je les définis généralement juste avant le .config()blocage de mon application pour un accès facile. Étant donné qu'il s'agit toujours de modules efficaces, vous devrez vous fier à l'injection de dépendances pour les utiliser, mais ils sont considérés comme «globaux» pour votre module d'application.

Par exemple:

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

Puis à l'intérieur de n'importe quel contrôleur:

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

De la question initiale est en fait des sons comme des propriétés statiques sont nécessaires ici de toute façon, soit comme mutable (valeur) ou finale (constante). C'est plus mon opinion personnelle qu'autre chose, mais je trouve que placer des éléments de configuration d'exécution sur le $rootScopedevient trop compliqué, trop rapidement.


la source
Je trouve le module de valeur très utile et concis. en particulier lorsque vous le liez à une variable $ scope dans un contrôleur, de sorte que les changements dans la logique ou la vue de ce contrôleur soient liés à la variable / valeur / service globale
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;
}]);

Dans votre HTML:

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

la source
8
localStorage.username = 'blah'

Si vous êtes assuré d'être sur un navigateur moderne. Mais sachez que vos valeurs seront toutes transformées en chaînes.

A également l'avantage d'être mis en cache entre les rechargements.

Kevin
la source
5
Hé, je vais stocker toutes les variables via localStorage. Nous aurons même une sorte de persistance alors! De plus, pour contourner la limitation de chaîne, stockons le .toString () d'une fonction, puis évaluons-le si nécessaire!
Shaz
comme déjà mentionné par @Shaz, cela a le problème de la persistance
Żubrówka
7

Veuillez me corriger si je me trompe, mais quand Angular 2.0 sera disponible, je ne pense pas que ce $rootScopesera le cas. Ma conjecture est basée sur le fait que cela $scopeest également supprimé. De toute évidence, les contrôleurs existeront toujours, mais pas à la mode. ng-controllerPensez plutôt à injecter des contrôleurs dans les directives. Au fur et à mesure que la version arrive, il est préférable d'utiliser les services comme variables globales si vous souhaitez passer plus facilement de la version 1.X à la version 2.0.

jason328
la source
Je suis d'accord, mettre des données directement sur $ rootScope va à l'encontre du but d'Angular. Prenez un peu de temps et organisez votre code correctement et cela vous aidera, non seulement dans la mise à niveau d'AngularJS 2.x, mais généralement tout au long du développement de votre application à mesure qu'elle se complexifie.
CatalinBerta
3
Si $ rootScope est supprimé, écrivez simplement un nouveau service appelé "$ rootScope" :)
uylmz
3

Vous pouvez également utiliser la variable d'environnement $windowafin qu'une variable globale déclare à l'extérieur d'un contrôleur puisse être vérifiée à l'intérieur d'un$watch

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

Attention, le cycle de résumé est plus long avec ces valeurs globales, il n'est donc pas toujours mis à jour en temps réel. Je dois enquêter sur ce temps de résumé avec cette configuration.

Megaman
la source
0

Je viens de trouver une autre méthode par erreur:

Ce que j'ai fait était de déclarer une var db = nulldéclaration d'application ci - dessus, puis de la modifier dans le app.jspuis lorsque j'y ai accédé dans le controller.js j'ai pu y accéder sans aucun problème. Il pourrait y avoir des problèmes avec cette méthode que je ne connais pas, mais c'est une bonne solution, je suppose.

Black Mamba
la source
0

Essayez ceci, vous ne forcerez pas à injecter $rootScopedans le contrôleur.

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

Vous ne pouvez l'utiliser que dans le bloc d'exécution car le bloc de configuration ne vous fournira pas d'utiliser le service de $ rootScope.

Rahul Murari
la source
0

C'est en fait assez simple. (Si vous utilisez Angular 2+ de toute façon.)

Ajoutez simplement

declare var myGlobalVarName;

Quelque part en haut de votre fichier de composant (comme après les instructions "import"), et vous pourrez accéder à "myGlobalVarName" n'importe où à l'intérieur de votre composant.

Andy Corman
la source
-2

Vous pouvez également faire quelque chose comme ça ..

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

function MyCtrl2($scope) {
    var name = $rootScope.$root.name;
}
pkdkk
la source
3
$ rootScope n'est pas défini lorsque vous l'utilisez de cette façon.
Ahmad Ahmadi