Globale Variablen in AngularJS

348

Ich habe ein Problem, bei dem ich eine Variable für den Bereich in einem Controller initialisiere. Dann wird es in einem anderen Controller geändert, wenn sich ein Benutzer anmeldet. Diese Variable wird verwendet, um Dinge wie die Navigationsleiste zu steuern und den Zugriff auf Teile der Site abhängig vom Benutzertyp zu beschränken. Daher ist es wichtig, dass der Wert beibehalten wird. Das Problem dabei ist, dass der Controller, der es initialisiert, erneut durch Winkel wie aufgerufen wird und dann die Variable auf ihren Anfangswert zurücksetzt.

Ich gehe davon aus, dass dies nicht die richtige Art ist, globale Variablen zu deklarieren und zu initialisieren. Nun, es ist nicht wirklich global. Meine Frage ist also, wie es richtig ist und gibt es gute Beispiele dafür, wie man mit der aktuellen Version von Angular arbeitet.

Glühbirne1
quelle
12
Da dies das erste Google-Ergebnis ist: Sie können jetzt app.constant () und app.value () verwenden, um app-weite Konstanten und Variablen zu erstellen. Mehr hier: bit.ly/1P51PED
Mroz

Antworten:

491

Sie haben grundsätzlich 2 Optionen für "globale" Variablen:

$rootScopeist ein übergeordnetes Element aller Bereiche, sodass die dort offengelegten Werte in allen Vorlagen und Controllern sichtbar sind. Die Verwendung von $rootScopeist sehr einfach, da Sie es einfach in einen beliebigen Controller einspeisen und Werte in diesem Bereich ändern können. Es mag praktisch sein, hat aber alle Probleme globaler Variablen .

Services sind Singletons, die Sie in jeden Controller einfügen und deren Werte im Bereich eines Controllers verfügbar machen können. Services, Singletons zu sein, sind immer noch "global", aber Sie haben eine weitaus bessere Kontrolle darüber, wo diese verwendet und verfügbar gemacht werden.

Die Nutzung von Diensten ist etwas komplexer, aber nicht so viel. Hier ein Beispiel:

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

und dann in einer Steuerung:

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

Hier ist die funktionierende jsFiddle: http://jsfiddle.net/pkozlowski_opensource/BRWPM/2/

pkozlowski.opensource
quelle
141
Aus den Angular-FAQ : Erstellen Sie umgekehrt keinen Dienst, dessen einziger Lebenszweck darin besteht, Datenbits zu speichern und zurückzugeben.
Jakob Stoeck
7
Ich verwende den Express + Angular-Samen von Btford . Wenn ich eine Variable auf dem $ rootScope in Controller1 setze und zu einer anderen URL wechsle (zB von Controller2), kann ich auf diese Variable zugreifen. Wenn ich jedoch die Seite auf Controller2 aktualisiere, ist die Variable unter $ rootScope nicht mehr verfügbar. Wenn ich Benutzerdaten bei der Anmeldung speichere, wie kann ich sicherstellen, dass diese Daten auch bei der Seitenaktualisierung an anderer Stelle verfügbar sind?
Craig Myles
19
@JakobStoeck Kombinieren Sie dies mit dieser Antwort und Sie müssen die Daten auf dem rootScope ablegen. Ich denke auch nicht, dass das richtig ist. Wie können global verwendete Datenbits im Winkel gespeichert und zurückgegeben werden?
user2483724
11
Ich bin gespannt, welche Nachteile ein Dienst hat, der speziell zum Speichern von Werten dient. Isoliert, nur dort injiziert, wo Sie es brauchen, und leicht testbar. Was ist der Nachteil?
Brian
12
Oh Gott, warum jeder Angst vor echten globalen Variablen hat, wenn Sie wissen, woraus Ihre App bestehen wird, machen Sie einfach eine echte globale js-Variable, anstatt Dinge zu komplizieren
Max Yari
93

Wenn Sie nur einen Wert speichern möchten, sollten Sie gemäß der Angular-Dokumentation zu Anbietern das Wertrezept verwenden:

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

Verwenden Sie es dann in einem Controller wie diesem:

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

Dasselbe kann mit einem Provider, einer Factory oder einem Service erreicht werden, da es sich um "nur syntaktischen Zucker über einem Provider-Rezept" handelt. Mit Value wird jedoch mit minimaler Syntax das erreicht, was Sie wollen.

Die andere Option ist die Verwendung $rootScope, aber es ist nicht wirklich eine Option, da Sie sie nicht aus den gleichen Gründen verwenden sollten, aus denen Sie keine globalen Variablen in anderen Sprachen verwenden sollten. Es wird empfohlen , sparsam zu verwenden.

Da alle Bereiche von erben, $rootScopetreten Probleme auf , wenn Sie eine Variable haben $rootScope.dataund jemand vergisst, dass diese databereits definiert ist und $scope.datain einem lokalen Bereich erstellt wird.


Wenn Sie diesen Wert ändern möchten und ihn auf allen Ihren Controllern beibehalten möchten, verwenden Sie ein Objekt und ändern Sie die Eigenschaften. Beachten Sie dabei, dass Javascript als "Kopie einer Referenz" übergeben wird :

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

JSFiddle-Beispiel

Dean Or. En
quelle
1
Wenn ich den Wert in einer Ansicht geändert habe, ist der neue Wert nicht dauerhaft. Woher? Können Sie bitte Ihre Antwort aktualisieren und uns mitteilen, wie Ihre Antwort clientIdaktualisiert werden kann?
Blaise
@ DeanOr Ist es möglich, den aktualisierten Wert zwischen den Aktualisierungen der Seite beizubehalten? Der Wert wird neu initialisiert, wenn ich die Seite aktualisiere.
Nabarun
Sie müssen dafür etwas anderes verwenden, z. B. ein Cookie, einen lokalen Speicher oder eine Datenbank.
Dean oder
1
Ich mag dieses hier am liebsten. Ich kann jetzt durch Build-Prozess für dev, stage und prod
jemiloii
1
Es gibt einen einfachen Artikel, der die Verwendung von Konstanten und Werten ähnlich wie bei globalen Variablen erklärt: ilikekillnerds.com/2014/11/…
RushabhG
36

Beispiel für AngularJS "globale Variablen" mit $rootScope:

Controller 1 setzt die globale Variable:

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

Controller 2 liest die globale Variable:

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

Hier ist eine funktionierende jsFiddle: http://jsfiddle.net/natefriedman/3XT3F/1/

NateFriedman
quelle
2
Dies funktioniert nicht in Ansichten von Richtlinien mit isoliertem Geltungsbereich. Siehe jsfiddle.net/3XT3F/7 . Sie können jedoch $ root verwenden, um dies zu umgehen. Siehe jsfiddle.net/3XT3F/10 . Vielen Dank an @natefaubion für den Hinweis
Tony Lâmpada
2
Beim Aktualisieren wäre der Wert von $ rootScope leer.
Xyonme
Die Hardcodierung von $ rootScope.name entspricht einem bestimmten Wert. Eigentlich müssen wir ihn lesen und dort festlegen.
25

Im Interesse einer weiteren Idee zum Wiki-Pool, aber was ist mit AngularJS valueund constantModulen? Ich fange gerade erst an, sie selbst zu verwenden, aber es klingt für mich so, als wären dies wahrscheinlich die besten Optionen hier.

Hinweis: Zum Zeitpunkt des Schreibens ist Angular 1.3.7 der neueste Stall. Ich glaube, diese wurden in 1.2.0 hinzugefügt, haben dies jedoch mit dem Änderungsprotokoll nicht bestätigt.

Abhängig davon, wie viele Sie definieren müssen, möchten Sie möglicherweise eine separate Datei für sie erstellen. Aber ich definiere diese im Allgemeinen kurz vor dem .config()Blockieren meiner App für den einfachen Zugriff. Da dies immer noch effektiv Module sind, müssen Sie sich auf die Abhängigkeitsinjektion verlassen, um sie zu verwenden, aber sie werden für Ihr App-Modul als "global" betrachtet.

Zum Beispiel:

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

Dann in jedem Controller:

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

Von der ersten Frage an klingt es eigentlich so, als wären hier ohnehin statische Eigenschaften erforderlich, entweder als veränderlich (Wert) oder endgültig (Konstante). Es ist mehr meine persönliche Meinung als alles andere, aber ich finde es $rootScopezu chaotisch, Laufzeitkonfigurationselemente zu schnell zu platzieren.


quelle
Ich finde das Wertemodul sehr nützlich und prägnant. insbesondere, wenn Sie es an eine $ scope-Variable innerhalb eines Controllers binden, sodass Änderungen innerhalb der Logik oder Ansicht dieses Controllers an die globale Variable / den Wert / den Dienst gebunden sind
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;
}]);

In Ihrem HTML:

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

quelle
8
localStorage.username = 'blah'

Wenn Sie garantiert in einem modernen Browser sind. Obwohl Sie wissen, dass Ihre Werte alle in Zeichenfolgen umgewandelt werden.

Hat auch den praktischen Vorteil, zwischen dem Nachladen zwischengespeichert zu werden.

Kevin
quelle
5
Heh, ich werde alle Variablen über localStorage speichern. Wir werden dann sogar eine Art von Ausdauer haben! Um die Zeichenfolgenbeschränkung zu umgehen, speichern wir die .toString () einer Funktion und bewerten sie bei Bedarf!
Shaz
Wie bereits von @Shaz erwähnt, hat dies das Problem der Beständigkeit
Żubrówka
7

Bitte korrigieren Sie mich, wenn ich falsch liege, aber wenn Angular 2.0 veröffentlicht $rootScopewird, glaube ich nicht, dass es etwas geben wird. Meine Vermutung basiert auf der Tatsache, dass $scopeauch entfernt wird. Offensichtlich werden Controller immer noch existieren, nur nicht in der Art und Weise. ng-controllerDenken Sie daran, Controller stattdessen in Direktiven einzufügen. Da die Veröffentlichung unmittelbar bevorsteht, ist es am besten, Dienste als globale Variablen zu verwenden, wenn Sie den Wechsel von Version 1.X zu Version 2.0 vereinfachen möchten.

jason328
quelle
Ich bin damit einverstanden, dass das direkte Speichern von Daten auf $ rootScope gegen Angulars gesamten Zweck verstößt. Nehmen Sie sich etwas Zeit und organisieren Sie Ihren Code richtig. Dies hilft Ihnen nicht nur beim AngularJS 2.x-Upgrade, sondern im Allgemeinen während der Entwicklung Ihrer App, wenn diese komplexer wird.
CatalinBerta
3
Wenn $ rootScope entfernt wird, schreiben Sie einfach einen neuen Dienst namens "$ rootScope" :)
uylmz
3

Sie können auch die Umgebungsvariable verwenden, $windowdamit eine außerhalb eines Controllers deklarierte globale Variable innerhalb eines Controllers überprüft werden kann$watch

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

Seien Sie vorsichtig, der Digest-Zyklus ist mit diesen globalen Werten länger, sodass er nicht immer in Echtzeit aktualisiert wird. Ich muss diese Digest-Zeit mit dieser Konfiguration untersuchen.

Megaman
quelle
0

Ich habe versehentlich eine andere Methode gefunden:

Was ich getan habe, war, eine var db = nullobige App-Deklaration zu deklarieren und sie dann zu ändern, app.jsals ich darauf zugegriffen habe. controller.js Ich konnte problemlos darauf zugreifen. Es könnte einige Probleme mit dieser Methode geben, die mir nicht bekannt sind, aber es ist Eine gute Lösung, denke ich.

Schwarze Mamba
quelle
0

Versuchen Sie dies, Sie werden nicht zwingen, $rootScopein Controller zu injizieren .

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

Sie können es nur im Ausführungsblock verwenden, da der Konfigurationsblock Ihnen nicht die Verwendung des Dienstes $ rootScope ermöglicht.

Rahul Murari
quelle
0

Es ist eigentlich ziemlich einfach. (Wenn Sie Angular 2+ trotzdem verwenden.)

Einfach hinzufügen

declare var myGlobalVarName;

Irgendwo oben in Ihrer Komponentendatei (z. B. nach den "import" -Anweisungen) können Sie überall in Ihrer Komponente auf "myGlobalVarName" zugreifen.

Andy Corman
quelle
-2

Sie können auch so etwas tun ..

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

function MyCtrl2($scope) {
    var name = $rootScope.$root.name;
}
pkdkk
quelle
3
$ rootScope ist undefiniert, wenn Sie es auf diese Weise verwenden.
Ahmad Ahmadi