Variables globales en AngularJS

348

Tengo un problema en el que estoy inicializando una variable en el alcance en un controlador. Luego, se cambia en otro controlador cuando un usuario inicia sesión. Esta variable se usa para controlar cosas como la barra de navegación y restringe el acceso a partes del sitio dependiendo del tipo de usuario, por lo que es importante que mantenga su valor. El problema con esto es que el controlador que lo inicializa, vuelve a ser llamado por angular de alguna manera y luego restablece la variable a su valor inicial.

Supongo que esta no es la forma correcta de declarar e inicializar variables globales, bueno, no es realmente global, así que mi pregunta es cuál es la forma correcta y ¿hay algún buen ejemplo en torno a eso que funcione con la versión actual de angular?

Bombilla1
fuente
12
Dado que este es el resultado número 1 de Google: ahora puede usar app.constant () y app.value () para crear constantes y variables para toda la aplicación. Más aquí: bit.ly/1P51PED
Mroz

Respuestas:

491

Tiene básicamente 2 opciones para variables "globales":

$rootScopees un padre de todos los ámbitos, por lo que los valores expuestos allí serán visibles en todas las plantillas y controladores. Usar el $rootScopees muy fácil ya que simplemente puede inyectarlo en cualquier controlador y cambiar los valores en este ámbito. Puede ser conveniente pero tiene todos los problemas de las variables globales .

Los servicios son singletons que puede inyectar a cualquier controlador y exponer sus valores en el alcance de un controlador. Los servicios, al ser singletons, siguen siendo 'globales', pero tienes un control mucho mejor sobre dónde se usan y se exponen.

El uso de servicios es un poco más complejo, pero no tanto, aquí hay un ejemplo:

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

y luego en un controlador:

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

Aquí está el jsFiddle de trabajo: http://jsfiddle.net/pkozlowski_opensource/BRWPM/2/

pkozlowski.opensource
fuente
141
De las preguntas frecuentes angulares : por el contrario, no cree un servicio cuyo único propósito en la vida sea almacenar y devolver bits de datos.
Jakob Stoeck
77
Estoy usando la semilla Express + Angular de Btford . Si configuro una variable en $ rootScope en Controller1, digamos y me muevo a otra url (con tecnología de Controller2 say) puedo acceder a esta variable. Sin embargo, si actualizo la página en Controller2, la variable ya no es accesible en $ rootScope. Si estoy guardando datos de usuario al iniciar sesión, ¿cómo puedo asegurarme de que se pueda acceder a estos datos en otro lugar incluso en la actualización de la página?
Craig Myles
19
@JakobStoeck Combina esto con esta respuesta y te quedará poner los datos en rootScope. Yo tampoco creo que sea correcto. ¿Cuál es la forma angular de almacenar y devolver bits de datos utilizados globalmente?
user2483724
11
Tengo curiosidad por saber cuáles son las desventajas de un servicio que es específicamente para almacenar valores. Aislado, solo inyectado donde lo necesita, y fácilmente comprobable. ¿Cuál es el inconveniente?
Brian
12
oh dios, por qué todo el mundo tiene miedo de volver a analizar las variables globales, si sabes en qué consistirá tu aplicación, simplemente crea una variable js global real en lugar de cosas
Max Yari
93

Si solo desea almacenar un valor, de acuerdo con la documentación angular de los proveedores , debe usar la receta del valor:

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

Luego úselo en un controlador como este:

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

Se puede lograr lo mismo utilizando un Proveedor, Fábrica o Servicio, ya que son "solo azúcar sintáctico sobre la receta de un proveedor", pero el uso de Value logrará lo que desea con una sintaxis mínima.

La otra opción es usar $rootScope, pero en realidad no es una opción porque no debe usarla por las mismas razones por las que no debería usar variables globales en otros idiomas. Se recomienda usar con moderación.

Dado que todos los ámbitos heredan $rootScope, si tiene una variable $rootScope.datay alguien olvida que dataya está definida y crea $scope.dataen un ámbito local, se encontrará con problemas.


Si desea modificar este valor y hacer que persista en todos sus controladores, use un objeto y modifique las propiedades teniendo en cuenta que Javascript se pasa por "copia de una referencia" :

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

JSFiddle ejemplo

Dean Or
fuente
1
Cuando cambié el valor en una vista, el nuevo valor no es persistente. ¿Cómo? ¿Puede actualizar su respuesta y dejarnos ver cómo clientIdse puede actualizar?
Blaise
@DeanOr ¿Es posible preservar el valor actualizado entre actualizaciones de la página? El valor se reinicializa si actualizo la página.
Nabarun
Tendrá que usar algo más para eso, como una cookie, almacenamiento local o base de datos.
Dean o
1
Realmente me gusta este el mejor. Ahora puedo modificar por proceso de compilación para dev, stage y prod
jemiloii
1
Hay un artículo simple que explica el uso de constantes y valores similares a las variables globales: ilikekillnerds.com/2014/11/…
RushabhG
36

Ejemplo de "variables globales" de AngularJS usando $rootScope:

El controlador 1 establece la variable global:

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

El controlador 2 lee la variable global:

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

Aquí hay un jsFiddle que funciona: http://jsfiddle.net/natefriedman/3XT3F/1/

NateFriedman
fuente
2
Esto no funciona en vistas de directivas de ámbito aislado. Ver jsfiddle.net/3XT3F/7 . Pero puede usar $ root para solucionarlo. Ver jsfiddle.net/3XT3F/10 . Gracias a @natefaubion por señalarlo
Tony Lâmpada
2
al actualizar, el valor de $ rootScope estaría vacío.
xyonme
codificar el $ rootScope.name es igual a algún valor ... en realidad necesitamos leerlo y configurarlo allí.
25

Con el interés de agregar otra idea al grupo wiki, pero ¿qué pasa con AngularJS valuey los constantmódulos? Recién estoy empezando a usarlos, pero me parece que estas son probablemente las mejores opciones aquí.

Nota: al momento de escribir, Angular 1.3.7 es la última versión estable, creo que se agregaron en 1.2.0, aunque no lo he confirmado con el registro de cambios.

Dependiendo de cuántos necesite definir, es posible que desee crear un archivo separado para ellos. Pero generalmente los defino justo antes del .config()bloqueo de mi aplicación para facilitar el acceso. Debido a que estos todavía son módulos efectivos, deberá confiar en la inyección de dependencia para usarlos, pero se consideran "globales" para su módulo de aplicación.

Por ejemplo:

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

Luego dentro de cualquier controlador:

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

De la pregunta inicial, en realidad, parece que las propiedades estáticas se requieren aquí de todos modos, ya sea como mutable (valor) o final (constante). Es más mi opinión personal que cualquier otra cosa, pero encuentro que colocar elementos de configuración de tiempo de ejecución se $rootScopevuelve demasiado complicado y demasiado rápido.


fuente
El módulo de valor me parece muy útil y conciso. especialmente cuando se enlaza a una variable $ alcance dentro de un controlador, de manera que los cambios dentro de la lógica o la opinión de que el controlador se unen a la variable / valor / servicio 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;
}]);

En tu HTML:

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

fuente
8
localStorage.username = 'blah'

Si tiene la garantía de estar en un navegador moderno. Aunque sepa que sus valores se convertirán en cadenas.

También tiene la ventaja práctica de estar en caché entre recargas.

Kevin
fuente
55
Je, voy a almacenar todas las variables a través de localStorage. ¡Incluso tendremos algún tipo de persistencia entonces! Además, para evitar la limitación de la cadena, almacenemos el .toString () de una función y luego evalúelo cuando sea necesario.
Shaz
como ya lo mencionó @Shaz, esto tiene el problema de la persistencia
Żubrówka
7

Corríjame si me equivoco, pero cuando se publique Angular 2.0, no creo $rootScopeque esté disponible. Mi conjetura se basa en el hecho de que también $scopese está eliminando. Obviamente, los controladores seguirán existiendo, pero no de la ng-controllermoda. Piense en inyectar controladores en las directivas. Como el lanzamiento es inminente, será mejor usar los servicios como variables globales si desea un momento más fácil para cambiar de la versión 1.X a la 2.0.

jason328
fuente
Estoy de acuerdo, poner datos directamente en $ rootScope va en contra del propósito de Angular. Tómese un poco de tiempo y organice su código correctamente y lo ayudará, no solo en la actualización de AngularJS 2.x, sino en general a lo largo del desarrollo de su aplicación a medida que se vuelve más complejo.
CatalinBerta
3
Si se elimina $ rootScope, simplemente escriba un nuevo servicio llamado "$ rootScope" :)
uylmz
3

También puede usar la variable de entorno $windowpara que una variable global declarar fuera de un controlador pueda verificarse dentro de un$watch

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

Con éxito, el ciclo de resumen es más largo con estos valores globales, por lo que no siempre se actualiza en tiempo real. Necesito investigar sobre ese tiempo de resumen con esta configuración.

Megaman
fuente
0

Acabo de encontrar otro método por error:

Lo que hice fue declarar una var db = nulldeclaración de aplicación anterior y luego modificarla en el app.jsmomento en que accedí a ella, controller.js pude acceder sin ningún problema. Puede haber algunos problemas con este método que no conozco, pero es Una buena solución, supongo.

Mamba negro
fuente
0

Pruebe esto, no obligará a inyectar $rootScopeen el controlador.

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

Solo puede usarlo en el bloque de ejecución porque el bloque de configuración no le proporcionará el servicio de $ rootScope.

Rahul Murari
fuente
0

En realidad es bastante fácil. (Si está utilizando Angular 2+ de todos modos).

Simplemente agregue

declare var myGlobalVarName;

En algún lugar en la parte superior de su archivo de componente (como después de las declaraciones de "importación"), podrá acceder a "myGlobalVarName" en cualquier lugar dentro de su componente.

Andy Corman
fuente
-2

También puedes hacer algo como esto ...

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

function MyCtrl2($scope) {
    var name = $rootScope.$root.name;
}
pkdkk
fuente
3
$ rootScope no está definido cuando lo usa de esta manera.
Ahmad Ahmadi