Comment puis-je déclarer une variable globale dans Angular 2 / Typescript? [fermé]

164

Je voudrais certaines variables soient accessibles partout dans un Angular 2dans la Typescriptlangue. Comment dois-je procéder pour y parvenir?

supercobra
la source
2
S'il s'agit de variables statiques, il n'est pas nécessaire d'utiliser des services. Ajoutez simplement une variable dans un fichier, puis importez-la partout où vous en avez besoin.
Eric Martinez
Malheureusement Angular2 ayant une exception au moment de l'exécution, en disant Uncaught ReferenceError: Settings is not defined. La classe Settingsavec des variables statiques publiques est définie pour exporter et importée là où elle a été utilisée.
Sen Jacob
Je sais que cet article est ancien et qu'il existe de nombreuses réponses valables. Mais comme Eric l'a mentionné. Si c'est une valeur simple que vous souhaitez déclarer et à laquelle vous avez accès via votre application, vous pouvez créer une classe et exporter la classe avec une propriété statique. Les variables statiques sont associées à une classe plutôt qu'à une instance de la classe. Vous pouvez importer la classe et pourrez accéder à la propriété depuis la classe directement.
De Wet Ellis

Réponses:

195

Voici la solution la plus simple sans Serviceni Observer:

Mettez les variables globales dans un fichier et exportez-les.

//
// ===== File globals.ts    
//
'use strict';

export const sep='/';
export const version: string="22.2.2";    

Pour utiliser des globaux dans un autre fichier, utilisez une importinstruction: import * as myGlobals from 'globals';

Exemple:

// 
// ===== File heroes.component.ts    
//
import {Component, OnInit} from 'angular2/core';
import {Router} from 'angular2/router';
import {HeroService} from './hero.service';
import {HeroDetailComponent} from './hero-detail.component';
import {Hero} from './hero';
import * as myGlobals from 'globals'; //<==== this one (**Updated**)

export class HeroesComponent implements OnInit {
    public heroes: Hero[];
    public selectedHero: Hero;
    // 
    //
    // Here we access the global var reference.
    //  
    public helloString: string="hello " + myGlobals.sep + " there";

         ...

        }
    }

Merci @ eric-martinez

supercobra
la source
3
Vous avez une erreur sur l'instruction d'importation. devait utiliserimport * as globs from 'globals'
Mike M
13
Pourquoi avez-vous utilisé "require" au lieu d'importer depuis?
Ayyash
4
J'utiliserais à la export constplace de export var- vous voulez vraiment vous assurer que ces variables globales ne peuvent pas être modifiées
Michal Boska
1
Une telle importation n'est plus valide dans TypeScript. Veuillez mettre à jour votre réponse. Correct serait:import * as myGlobals from 'globals'
Mick
7
import * as myGlobals from './path/to/globals';
Timothy Zorn
89

J'aime aussi la solution de @supercobra. Je voudrais juste l'améliorer légèrement. Si vous exportez un objet contenant toutes les constantes, vous pouvez simplement utiliser es6 importer le module sans utiliser require .

J'ai également utilisé Object.freeze pour que les propriétés deviennent de vraies constantes. Si le sujet vous intéresse, vous pouvez lire cet article .

// global.ts

 export const GlobalVariable = Object.freeze({
     BASE_API_URL: 'http://example.com/',
     //... more of your variables
 });

Reportez-vous au module en utilisant l'importation.

//anotherfile.ts that refers to global constants
import { GlobalVariable } from './path/global';

export class HeroService {
    private baseApiUrl = GlobalVariable.BASE_API_URL;

    //... more code
}
Tim Hong
la source
c'est pour moi la meilleure solution car (1) c'est la plus simple avec le moins de code et (2) cela ne vous oblige pas à injecter un service sacré dans chaque composant ou endroit dans lequel vous voulez l'utiliser, ni vous oblige à l'enregistrer dans @NgModule. Je ne peux pas pour la vie de moi comprendre pourquoi il serait nécessaire de créer un service Angular 2 pour faire cela, mais peut-être y a-t-il quelque chose que je néglige? J'utilise cette excellente solution pour le moment, mais s'il vous plaît laissez-moi savoir pourquoi les autres réponses plus compliquées ici sont meilleures?
FireDragon
8
Votre GlobalVariable n'est pas une variable. C'est une constante.
Priya R
@PriyaR LOL, oui, vous avez raison. J'ai supposé que l'objectif principal de la question était d'avoir un moyen sûr d'accéder à certaines valeurs à l'échelle mondiale, alors j'ai improvisé. Sinon, n'hésitez pas à changer const en var, vous obtenez votre variable.
Tim Hong
L'inconvénient d'Object.freeze est que les valeurs ne sont pas tapées. Quoi qu'il en soit, envelopper les valeurs dans une classe est une meilleure conception de mon point de vue. Il faut donc choisir entre les propriétés typées et les vraies constantes.
Harpes
comment définir GlobalVariable.BASE_API_URL dans un autre composant ..?
Sunil Chaudhry
59

Un service partagé est la meilleure approche

export class SharedService {
  globalVar:string;
}

Mais vous devez être très prudent lors de son enregistrement pour pouvoir partager une seule instance pour l'ensemble de votre application. Vous devez le définir lors de l'enregistrement de votre candidature:

bootstrap(AppComponent, [SharedService]);

mais pas pour le redéfinir dans les providersattributs de vos composants:

@Component({
  (...)
  providers: [ SharedService ], // No
  (...)
})

Sinon, une nouvelle instance de votre service sera créée pour le composant et ses sous-composants.

Vous pouvez consulter cette question concernant le fonctionnement de l'injection de dépendances et des injecteurs hiérarchiques dans Angular2:

Vous pouvez remarquer que vous pouvez également définir des Observablepropriétés dans le service pour notifier certaines parties de votre application lorsque vos propriétés globales changent:

export class SharedService {
  globalVar:string;
  globalVarUpdate:Observable<string>;
  globalVarObserver:Observer;

  constructor() {
    this.globalVarUpdate = Observable.create((observer:Observer) => {
      this.globalVarObserver = observer;
    });
  }

  updateGlobalVar(newValue:string) {
    this.globalVar = newValue;
    this.globalVarObserver.next(this.globalVar);
  }
}

Voir cette question pour plus de détails:

Thierry Templier
la source
Il semble que celui-ci soit différent. Semble @ Rat2000 pense que notre réponse est fausse. Je laisse généralement cette décision à d'autres que peaple fournissant des réponses concurrentes, mais s'il est convaincu que nos réponses sont fausses, je pense que c'est valable. Les documents auxquels il a lié dans un commentaire à ma réponse mentionnent que c'est DÉCOURAGÉ mais je ne vois aucun inconvénient et les arguments dans les documents sont assez faibles. Il est également assez courant d'ajouter des fournisseurs à bootstrap. Quel serait le but de cet argument de toute façon. Et qu'en est-il HTTP_PROVIDERSet similaires, ne devraient-ils pas non plus être ajoutés bootstrap()?
Günter Zöchbauer
2
Oui, je viens de lire l'argument et la section du doc. Honnêtement, je ne comprends pas vraiment pourquoi il est déconseillé à la doc. Est-ce un moyen de définir une division logique: ce qui est spécifique au cœur d'Angular2 (fournisseurs de routage, fournisseurs http) lors du démarrage et ce qui est spécifique à l'application dans l'injecteur de composant d'application. Cela dit, nous ne pouvons avoir qu'un sous-injecteur (celui de l'application) pour le root (défini lors du bootstrap). Est-ce que je rate quelque chose? De plus dans la doc concernant les injecteurs hiérarchiques, les prestataires sont définis au sein de l'injecteur racine ;-)
Thierry Templier
3
Le seul argument que je vois est que garder la portée aussi étroite que possible et utiliser le composant racine est au moins en théorie légèrement plus étroit que l'utilisation, bootstrap()mais en pratique, cela n'a pas d'importance. Je pense que les énumérer boostrap()rend le code plus facile à comprendre. Un composant a des fournisseurs, des directives, un modèle. Je trouve cela surchargé sans les fournisseurs mondiaux qui y figurent également. Par conséquent, je préfère bootstrap().
Günter Zöchbauer
2
et comment référencer ces variables globales? même après avoir amorcé le service, l'appel alert(globalVar)entraîne une erreur.
phil294
Je n'ai pas encore essayé cela, mais vous voudrez quelque chose comme: alert (this.SharedService.globalVar)
trees_are_great
39

Voir par exemple Angular 2 - Implémentation de services partagés

@Injectable() 
export class MyGlobals {
  readonly myConfigValue:string = 'abc';
}

@NgModule({
  providers: [MyGlobals],
  ...
})

class MyComponent {
  constructor(private myGlobals:MyGlobals) {
    console.log(myGlobals.myConfigValue);
  }
}

ou fournir des valeurs individuelles

@NgModule({
  providers: [{provide: 'myConfigValue', useValue: 'abc'}],
  ...
})

class MyComponent {
  constructor(@Inject('myConfigValue') private myConfigValue:string) {
    console.log(myConfigValue);
  }
}
Günter Zöchbauer
la source
Depuis Angular2 beta 7 (je pense), vous ne devez pas enregistrer votre service directement dans le composant racine (aka bootstrap). Vous pouvez cependant y injecter un fournisseur spécifique si vous souhaitez remplacer quelque chose dans votre application.
Mihai
1
Pas sûr de ce que vous voulez dire. Bien sûr, vous pouvez enregistrer un service dans bootstrap(). bootstrap()et le composant racine sont deux choses différentes. Lorsque vous appelez, bootstrap(AppComponent, [MyService])vous enregistrez le service dans boostrap()et AppComponentest le composant racine. La documentation mentionne quelque part qu'il est préférable d'enregistrer les fournisseurs (service) dans les composants racine, providers: ['MyService']mais je n'ai encore trouvé aucun argument en faveur ou contre le bootstrap()composant racine.
Günter Zöchbauer
Vous pouvez trouver votre argument dans la section Angular 2guide Dependency Injection ( angular.io/docs/ts/latest/guide/dependency-injection.html ). Comme on dit, vous pouvez le faire mais c'est découragé. Cet utilisateur demande la meilleure façon d'injecter quelque chose alors que votre solution n'est clairement pas correcte. il en va de même pour @ThierryTemplier
Mihai
1
Je pense que la citation la plus importante du document Angular est "L'option du fournisseur d'amorçage est destinée à configurer et à remplacer les propres services préenregistrés d'Angular, tels que sa prise en charge du routage." Je mets rarement des services dans bootstrap moi-même, et je suis heureux de voir que la documentation le suggère maintenant.
Mark Rajcok
1
n'oubliez pas d'exporter la classe
Demodave
15

Créez la classe Globals dans app / globals.ts :

import { Injectable } from '@angular/core';

Injectable()
export class Globals{
    VAR1 = 'value1';
    VAR2 = 'value2';
}

Dans votre composant:

import { Globals } from './globals';

@Component({
    selector: 'my-app',
    providers: [ Globals ],
    template: `<h1>My Component {{globals.VAR1}}<h1/>`
})
export class AppComponent {
    constructor(private globals: Globals){
    }
}

Remarque : vous pouvez ajouter le fournisseur de services Globals directement au module au lieu du composant, et vous n'aurez pas besoin d'ajouter en tant que fournisseur à chaque composant de ce module.

@NgModule({
    imports: [...],
    declarations: [...],
    providers: [ Globals ],
    bootstrap: [ AppComponent ]
})
export class AppModule {
}
gradosevic
la source
C'est la meilleure réponse, car elle offre une approche plus portable que d'avoir à ajouter le service à chaque composant de l'application. Je vous remercie!
Vidal Quevedo
5
Le code fonctionne. Mais notez que l'injection d'une classe Globalsen l'ajoutant également à providers: [ ... ]signifie que vous ne pouvez pas modifier une valeur à l'intérieur d'un composant, puis demander la valeur mise à jour dans un deuxième composant. Chaque fois que vous injectez, Globalsc'est une nouvelle instance. Si vous souhaitez modifier ce comportement, ne l' ajoutez pas simplement en Globals tant que fournisseur.
Timo Bähr
juste une note, ça devrait être@Injectable()
mast3rd3mon
11

À mon humble avis pour Angular2 (v2.2.3), le meilleur moyen est d'ajouter des services contenant la variable globale et de les injecter dans les composants sans l' providersétiquette à l'intérieur de l' @Componentannotation. De cette manière, vous pouvez partager des informations entre les composants.

Un exemple de service qui possède une variable globale:

import { Injectable } from '@angular/core'

@Injectable()
export class SomeSharedService {
  public globalVar = '';
}

Un exemple de composant qui met à jour la valeur de votre variable globale:

import { SomeSharedService } from '../services/index';

@Component({
  templateUrl: '...'
})
export class UpdatingComponent {

  constructor(private someSharedService: SomeSharedService) { }

  updateValue() {
    this.someSharedService.globalVar = 'updated value';
  }
}

Un exemple de composant qui lit la valeur de votre variable globale:

import { SomeSharedService } from '../services/index';

@Component({
  templateUrl: '...'
})
export class ReadingComponent {

  constructor(private someSharedService: SomeSharedService) { }

  readValue() {
    let valueReadOut = this.someSharedService.globalVar;
    // do something with the value read out
  }
}

Notez que cela neproviders: [ SomeSharedService ] doit pas être ajouté à votre @Componentannotation. En n'ajoutant pas cette ligne, l'injection vous donnera toujours la même instance de SomeSharedService. Si vous ajoutez la ligne, une instance fraîchement créée est injectée.

Timo Bähr
la source
Mais sans ajouter la ligne de fournisseurs, j'ai eu une erreur comme celle-ci:Unhandled Promise rejection: No provider for SomeSharedService
Rocky
Je vois. Je devrais ajouter providers: [SomeSharedService]dans le fichier du module parent. Merci.
Rocky
Cela ne fonctionne pas lorsque nous chargeons des modules paresseux.
lpradhap
9

Je ne connais pas le meilleur moyen, mais le moyen le plus simple si vous souhaitez définir une variable globale à l'intérieur d'un composant est d'utiliser une windowvariable pour écrire comme ceci:

window.GlobalVariable = "what ever!"

vous n'avez pas besoin de le passer pour bootstrap ou l'importer ailleurs, et il est globalement accessible à tous les JS (pas seulement aux composants angular 2).

Mahdi Jadaliha
la source
1
Je dirais que c'est la pire façon. Utiliser une variable statique n'est pas plus compliqué mais n'est pas si moche non plus ;-)
Günter Zöchbauer
2
Je suis d'accord que cela rend la gestion difficile. Cependant j'ai fini par les utiliser en développement jusqu'à ce que je trouve ce que je voulais mettre dans les productions. Dans la variable statique, vous devez les importer encore et encore partout où vous voulez les utiliser, à côté d'un cas où je produisais ma vue en déplacement avec jquery dans des composants angulaires - il n'y avait pas de modèle, et pour ajouter des événements au DOM produit en utilisant statique la douleur est variable.
Mahdi Jadaliha
1
De plus, ce n'est pas statique, vous pouvez changer la valeur de partout!
Mahdi Jadaliha
1
Il détruit également le rendu côté serveur. Évitez de manipuler directement la fenêtre ou le document.
Erik Honn
1
D'accord. mais personnellement je ne suis aucune ligne directrice dans ma vie (si je pouvais faire mieux que ça).
Mahdi Jadaliha
7

C'est comme ça que je l'utilise:

global.ts

export var server: string = 'http://localhost:4200/';
export var var2: number = 2;
export var var3: string = 'var3';

pour l'utiliser, importez simplement comme ça:

import { Injectable } from '@angular/core';
import { Http, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import * as glob from '../shared/global'; //<== HERE

@Injectable()
export class AuthService {
    private AuhtorizationServer = glob.server
}

MODIFIÉ: préfixé "_" comme recommandé.

Guilherme Teubl
la source
N'utilisez pas "_" comme préfixe pour les propriétés privées. github.com/Microsoft/TypeScript/wiki/Coding-guidelines
crh225
4

Je pense que le meilleur moyen est de partager un objet avec des variables globales dans toute votre application en l'exportant et en l'important où vous le souhaitez.

Créez d'abord un nouveau fichier .ts par exemple globals.ts et déclarez un objet. Je lui ai donné un type d' objet mais vous pouvez également utiliser n'importe quel type ou {}

export let globalVariables: Object = {
 version: '1.3.3.7',
 author: '0x1ad2',
 everything: 42
};

Après cela, importez-le

import {globalVariables} from "path/to/your/globals.ts"

Et utilisez-le

console.log(globalVariables);
0x1ad2
la source
3

J'aime la réponse de @supercobra, mais j'utiliserais le mot-clé const car il est déjà disponible dans ES6:

//
// ===== File globals.ts    
//
'use strict';

export const sep='/';
export const version: string="22.2.2"; 
Zolcsi
la source