Variabili globali in AngularJS


348

Ho un problema in cui sto inizializzando una variabile nell'ambito di un controller. Quindi viene modificato in un altro controller quando un utente accede. Questa variabile viene utilizzata per controllare elementi come la barra di navigazione e limita l'accesso alle parti del sito in base al tipo di utente, quindi è importante che mantenga il suo valore. Il problema è che il controller che lo inizializza, viene richiamato di nuovo da angular come e quindi reimposta la variabile al suo valore iniziale.

Presumo che questo non sia il modo corretto di dichiarare e inizializzare le variabili globali, beh non è proprio globale, quindi la mia domanda è qual è il modo corretto e ci sono buoni esempi in giro che funzionano con l'attuale versione di angolare?


12
Dato che questo è il risultato google numero 1: ora puoi usare app.constant () e app.value () per creare costanti e variabili a livello di app. Altro qui: bit.ly/1P51PED
Mroz

Risposte:


491

Hai sostanzialmente 2 opzioni per le variabili "globali":

$rootScopeè un genitore di tutti gli ambiti, quindi i valori esposti lì saranno visibili in tutti i modelli e controller. L'uso di $rootScopeè molto semplice in quanto puoi semplicemente iniettarlo in qualsiasi controller e modificare i valori in questo ambito. Potrebbe essere conveniente ma ha tutti i problemi delle variabili globali .

I servizi sono singoli punti che è possibile iniettare in qualsiasi controller ed esporre i loro valori nell'ambito di un controller. I servizi, essendo i singoli, sono ancora "globali", ma hai un controllo molto migliore su dove vengono utilizzati ed esposti.

L'uso dei servizi è un po 'più complesso, ma non molto, ecco un esempio:

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

e quindi in un controller:

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

Ecco il jsFiddle funzionante: http://jsfiddle.net/pkozlowski_opensource/BRWPM/2/


141
Dalle FAQ angolari : al contrario, non creare un servizio il cui unico scopo nella vita è quello di archiviare e restituire bit di dati.
Jakob Stoeck,

7
Sto usando il seme Express + Angular di Btford . Se imposto una variabile su $ rootScope in Controller1 dire e passare a un altro URL (alimentato da Controller2 dire) posso accedere a questa variabile. Tuttavia, se aggiorno la pagina su Controller2, la variabile non è più accessibile su $ rootScope. Se sto salvando i dati utente al momento dell'accesso, come posso assicurarmi che questi dati siano accessibili altrove anche durante l'aggiornamento della pagina?
Craig Myles,

19
@JakobStoeck Combina questo con questa risposta e rimani con il mettere i dati sul rootScope. Non penso che sia giusto neanche. Qual è il modo angolare di archiviare e restituire bit di dati utilizzati a livello globale?
user2483724

11
Sono curioso di sapere quali sono gli svantaggi di un servizio specifico per la memorizzazione di valori. Isolato, iniettato solo dove serve e facilmente testabile. Qual è il rovescio della medaglia?
Brian,

12
oh dio, perché tutti hanno paura delle variabili globali reaal, se sai in cosa consisterà la tua app, basta creare una vera variabile js globale invece di cose complicate
Max Yari,

93

Se si desidera solo memorizzare un valore, in base alla documentazione Angolare sui provider , è necessario utilizzare la ricetta Valore:

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

Quindi usalo in un controller come questo:

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

La stessa cosa può essere raggiunta usando un fornitore, una fabbrica o un servizio poiché sono "solo zucchero sintattico in cima a una ricetta del fornitore", ma usando Value si otterrà ciò che si desidera con una sintassi minima.

L'altra opzione è usare $rootScope, ma non è davvero un'opzione perché non dovresti usarla per gli stessi motivi per cui non dovresti usare variabili globali in altre lingue. Si consiglia di usarlo con parsimonia.

Poiché tutti gli ambiti ereditano da $rootScope, se si dispone di una variabile $rootScope.datae qualcuno dimentica che dataè già definito e crea $scope.datain un ambito locale, si verificheranno problemi.


Se vuoi modificare questo valore e farlo persistere su tutti i tuoi controller, usa un oggetto e modifica le proprietà tenendo presente che Javascript è passato da "copia di un riferimento" :

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

JS Esempio di violino


1
Quando ho modificato il valore in una vista, il nuovo valore non è persistente. Come mai? Puoi aggiornare la tua risposta e farci vedere come clientIdpuoi essere aggiornato?
Blaise,

@ Deco O È possibile preservare il valore aggiornato tra gli aggiornamenti della pagina? Il valore viene reinizializzato se aggiorno la pagina.
Nabarun,

Dovrai usare qualcos'altro per questo come un cookie, una memoria locale o un database.
Decano o

1
Mi piace davvero molto questo. Ora posso modificare il processo di compilazione per dev, stage e prod
jemiloii il

1
C'è un semplice articolo che spiega l'uso di costanti e valori simili alle variabili globali: ilikekillnerds.com/2014/11/…
RushabhG

36

Esempio di "variabili globali" di AngularJS usando $rootScope:

Il controller 1 imposta la variabile globale:

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

Il controller 2 legge la variabile globale:

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

Ecco un jsFiddle funzionante: http://jsfiddle.net/natefriedman/3XT3F/1/


2
Ciò non funziona in vista di direttive di ambito isolate. Vedi jsfiddle.net/3XT3F/7 . Ma puoi usare $ root per aggirare il problema. Vedi jsfiddle.net/3XT3F/10 . Grazie a @natefaubion per averlo segnalato
Tony Lâmpada,

2
al momento dell'aggiornamento, il valore $ rootScope sarebbe vuoto.
xyonme,

hardcoding $ rootScope.name equivale a un valore ... in realtà dobbiamo leggerlo e impostarlo lì.

25

Nell'interesse di aggiungere un'altra idea al pool di wiki, ma che dire di AngularJS valuee dei constantmoduli? Sto solo iniziando a usarli da solo, ma mi sembra che queste siano probabilmente le migliori opzioni qui.

Nota: al momento della stesura di questo articolo, Angular 1.3.7 è l'ultimo stabile, credo che questi siano stati aggiunti in 1.2.0, ma non l'ho confermato con il log delle modifiche.

A seconda di quanti ne devi definire, potresti creare un file separato per loro. Ma generalmente li definisco appena prima del .config()blocco della mia app per un facile accesso. Poiché si tratta ancora di moduli efficaci, per utilizzarli dovrai fare affidamento sull'iniezione di dipendenza, ma sono considerati "globali" per il modulo dell'app.

Per esempio:

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

Quindi all'interno di qualsiasi controller:

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

Dalla domanda iniziale in realtà sembra che qui siano richieste proprietà statiche, sia come mutabili (valore) che finali (costanti). È più la mia opinione personale di ogni altra cosa, ma trovo che posizionare gli elementi della configurazione di runtime su $rootScopediventi troppo disordinato, troppo rapidamente.


Trovo il modulo di valore molto utile e conciso. specialmente quando lo si associa a una variabile $ scope all'interno di un controller, in modo che le modifiche all'interno della logica o della vista di quel controller siano legate alla variabile / valore / servizio globali
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;
}]);

Nel tuo HTML:

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

8
localStorage.username = 'blah'

Se sei sicuro di essere su un browser moderno. Anche se sai che i tuoi valori saranno tutti trasformati in stringhe.

Ha anche il pratico vantaggio di essere memorizzato nella cache tra le ricariche.


5
Heh, ho intenzione di memorizzare tutte le variabili tramite localStorage. Avremo anche una sorta di persistenza allora! Inoltre, per aggirare il limite di stringhe, memorizziamo il .toString () di una funzione e valutiamolo quando necessario!
Shaz,

come già accennato da @Shaz, questo ha il problema della persistenza
Żubrówka

7

Per favore, correggimi se sbaglio, ma quando viene rilasciato Angular 2.0 non credo che $rootScopeci sarà. La mia congettura si basa anche sul fatto che $scopeviene rimosso. Ovviamente i controller continueranno a esistere, ma non nella ng-controllermoda. Pensa invece di iniettare i controller nelle direttive. Poiché la versione arriverà imminente, sarà meglio usare i servizi come variabili globali se si desidera un tempo più semplice per passare dalla versione 1.X alla 2.0.


Sono d'accordo, mettere i dati direttamente su $ rootScope è contro l'intero scopo di Angular. Prenditi un po 'di tempo e organizza correttamente il tuo codice e ti aiuterà, non solo nell'aggiornamento di AngularJS 2.x, ma in generale durante lo sviluppo della tua app man mano che diventa più complesso.
CatalinBerta,

3
Se $ rootScope viene rimosso, basta scrivere un nuovo servizio chiamato "$ rootScope" :)
uylmz

3

È inoltre possibile utilizzare la variabile di ambiente in $windowmodo che una variabile globale dichiarata all'esterno di un controller possa essere verificata all'interno di a$watch

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

Inoltre, il ciclo digest è più lungo con questi valori globali, quindi non è sempre aggiornato in tempo reale. Ho bisogno di indagare su quel tempo digest con questa configurazione.


0

Ho appena trovato un altro metodo per errore:

Quello che ho fatto è stato dichiarare una var db = nulldichiarazione dell'app precedente e poi modificarla in quel app.jsmomento, quando l'ho acceduta, controller.js sono stata in grado di accedervi senza alcun problema. Potrebbero esserci problemi con questo metodo di cui non sono a conoscenza ma è una buona soluzione immagino.


0

Prova questo, non forzerai l'iniezione $rootScopenel controller.

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

Puoi usarlo solo nel blocco di esecuzione perché il blocco di configurazione non ti fornirà il servizio di $ rootScope.


0

In realtà è abbastanza facile. (Se stai usando Angular 2+ comunque.)

Aggiungi semplicemente

declare var myGlobalVarName;

Da qualche parte nella parte superiore del file del componente (ad esempio dopo le istruzioni "import"), e sarai in grado di accedere a "myGlobalVarName" ovunque all'interno del componente.


-2

Puoi anche fare qualcosa del genere ..

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

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

3
$ rootScope non è definito quando lo si utilizza in questo modo.
Ahmad Ahmadi,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.