web-dev-qa-db-fra.com

Définir les constantes globales dans Angular 2

Dans Angular 1.x, vous pouvez définir des constantes comme ceci:

angular.module('mainApp.config', [])
.constant('API_ENDPOINT', 'http://127.0.0.1:6666/api/')

Quel serait l'équivalent dans Angular2 (avec TypeScript)? Je ne veux simplement pas répéter l'URL de base de l'API encore et encore dans tous mes services.

206
AndreFeijo

Les modifications ci-dessous fonctionnent pour moi sur Angular 2 version finale:

export class AppSettings {
   public static API_ENDPOINT='http://127.0.0.1:6666/api/';
}

Et puis dans le service:

import {Http} from 'angular2/http';
import {Message} from '../models/message';
import {Injectable} from 'angular2/core';
import {Observable} from 'rxjs/Observable';
import {AppSettings} from '../appSettings';
import 'rxjs/add/operator/map';

@Injectable()
export class MessageService {

    constructor(private http: Http) { }

    getMessages(): Observable<Message[]> {
        return this.http.get(AppSettings.API_ENDPOINT+'/messages')
            .map(response => response.json())
            .map((messages: Object[]) => {
                return messages.map(message => this.parseData(message));
            });
    }

    private parseData(data): Message {
        return new Message(data);
    }
}
211
AndreFeijo

La solution pour la configuration fournie par l’équipe angulaire elle-même peut être trouvée ici .

Voici tout le code pertinent:

1) app.config.ts

import { OpaqueToken } from "@angular/core";

export let APP_CONFIG = new OpaqueToken("app.config");

export interface IAppConfig {
    apiEndpoint: string;
}

export const AppConfig: IAppConfig = {    
    apiEndpoint: "http://localhost:15422/api/"    
};

2) app.module.ts

import { APP_CONFIG, AppConfig } from './app.config';

@NgModule({
    providers: [
        { provide: APP_CONFIG, useValue: AppConfig }
    ]
})

3) votre.service.ts

import { APP_CONFIG, IAppConfig } from './app.config';

@Injectable()
export class YourService {

    constructor(@Inject(APP_CONFIG) private config: IAppConfig) {
             // You can use config.apiEndpoint now
    }   
}

Maintenant, vous pouvez injecter la configuration partout sans utiliser les noms de chaînes ni utiliser votre interface pour les contrôles statiques.

Vous pouvez bien sûr séparer l'interface et la constante afin de pouvoir fournir différentes valeurs en production et en développement, par exemple.

146
Ilya Chernomordik

Dans Angular2, vous avez la définition suivante: fourniture , qui vous permet de configurer différents types de dépendances:

provide(token: any, {useClass, useValue, useExisting, useFactory, deps, multi}

Comparaison avec Angular 1

app.service dans Angular1 équivaut à useClass dans Angular2.

app.factory dans Angular1 équivaut à useFactory dans Angular2.

app.constant et app.value ont été simplifiés à useValue avec moins de contraintes. c'est-à-dire qu'il n'y a plus de bloc config.

app.provider - Il n'y a pas d'équivalent dans Angular 2.

Exemples

Pour configurer avec l'injecteur de racine:

bootstrap(AppComponent,[provide(API_ENDPOINT, { useValue='http://127.0.0.1:6666/api/' })]);

Ou configuration avec l'injecteur de votre composant:

providers: [provide(API_ENDPOINT, { useValue: 'http://127.0.0.1:6666/api/'})]

provide est un raccourci pour:

var injectorValue = Injector.resolveAndCreate([
  new Provider(API_ENDPOINT, { useValue: 'http://127.0.0.1:6666/api/'})
]);

Avec l'injecteur, obtenir la valeur est facile:

var endpoint = injectorValue.get(API_ENDPOINT);
61
pixelbits

Dans Angular 4, vous pouvez utiliser la classe d'environnement pour conserver tous vos globals.

Vous avez environment.ts et environment.prod.ts par défaut.

Par exemple

export const environment = {
  production: false,
  apiUrl: 'http://localhost:8000/api/'
};

Et ensuite sur votre service:

import { environment } from '../../environments/environment';
...
environment.apiUrl;
46
Nacho

Mise à jour pour Angular 4+

Maintenant, nous pouvons simplement utiliser le fichier d'environnements que angular fournit par défaut si votre projet est généré via angular-cli.

par exemple 

Dans votre dossier environnements, créez les fichiers suivants 

  • environment.prod.ts
  • environment.qa.ts
  • environment.dev.ts

et chaque fichier peut contenir des modifications de code associées telles que:

  • environment.prod.ts

    export const environment = {
         production: true,
         apiHost: 'https://api.somedomain.com/prod/v1/',
         CONSUMER_KEY: 'someReallyStupidTextWhichWeHumansCantRead', 
         codes: [ 'AB', 'AC', 'XYZ' ],
    };
    
  • environment.qa.ts

    export const environment = {
         production: false,
         apiHost: 'https://api.somedomain.com/qa/v1/',
         CONSUMER_KEY : 'someReallyStupidTextWhichWeHumansCantRead', 
         codes: [ 'AB', 'AC', 'XYZ' ],
    };
    
  • environment.dev.ts

    export const environment = {
         production: false,
         apiHost: 'https://api.somedomain.com/dev/v1/',
         CONSUMER_KEY : 'someReallyStupidTextWhichWeHumansCantRead', 
         codes: [ 'AB', 'AC', 'XYZ' ],
    };
    

Cas d'utilisation en application

Vous pouvez importer des environnements dans n’importe quel fichier tel que services clientUtilServices.ts 

import {environment} from '../../environments/environment';

getHostURL(): string {
    return environment.apiHost;
  }

Cas d'utilisation dans la construction

Ouvrez votre fichier cli angulaire .angular-cli.json et dans "apps": [{...}] ajoutez le code suivant 

 "apps":[{
        "environments": {
            "dev": "environments/environment.ts",
            "prod": "environments/environment.prod.ts",
            "qa": "environments/environment.qa.ts",
           }
         }
       ]

Si vous voulez construire pour la production, lancez ng build --env=prod, il lira la configuration à partir de environment.prod.ts, comme vous pouvez le faire pour qa ou dev

## réponse plus ancienne

J'ai fait quelque chose comme ci-dessous, dans mon fournisseur:

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

@Injectable()
export class ConstantService {

API_ENDPOINT :String;
CONSUMER_KEY : String;

constructor() {
    this.API_ENDPOINT = 'https://api.somedomain.com/v1/';
    this.CONSUMER_KEY = 'someReallyStupidTextWhichWeHumansCantRead'
  }
}

Ensuite, j'ai accès à toutes les données constantes de n'importe où 

import {Injectable} from '@angular/core';
import {Http} from '@angular/http';
import 'rxjs/add/operator/map';

import {ConstantService} from  './constant-service'; //This is my Constant Service


@Injectable()
export class ImagesService {
    constructor(public http: Http, public ConstantService: ConstantService) {
    console.log('Hello ImagesService Provider');

    }

callSomeService() {

    console.log("API_ENDPOINT: ",this.ConstantService.API_ENDPOINT);
    console.log("CONSUMER_KEY: ",this.ConstantService.CONSUMER_KEY);
    var url = this.ConstantService.API_ENDPOINT;
    return this.http.get(url)
  }
 }
35
Anjum....

Bien que l'approche qui consiste à avoir une classe AppSettings avec une constante de chaîne telle que ApiEndpoint fonctionne, ce n'est pas idéal car nous ne pourrions pas échanger ce véritable ApiEndpoint contre d'autres valeurs au moment des tests unitaires. 

Nous devons être en mesure d'injecter ces points de terminaison api dans nos services (pensez à injecter un service dans un autre service). Nous n'avons pas non plus besoin de créer une classe entière pour cela. Tout ce que nous voulons, c'est injecter une chaîne dans nos services, qui est notre ApiEndpoint. Pour compléter le excellente réponse de pixelbits , voici le code complet expliquant comment procéder dans Angular 2:

Premièrement, nous devons dire à Angular comment fournir une instance de notre ApiEndpoint lorsque nous le demandons dans notre application (pensez-y que vous enregistrez une dépendance): 

bootstrap(AppComponent, [
        HTTP_PROVIDERS,
        provide('ApiEndpoint', {useValue: 'http://127.0.0.1:6666/api/'})
]);         


Et ensuite, dans le service, nous injectons cet ApiEndpoint dans le constructeur du service et Angular nous le fournira en fonction de notre enregistrement ci-dessus:

import {Http} from 'angular2/http';
import {Message} from '../models/message';
import {Injectable, Inject} from 'angular2/core';  // * We import Inject here
import {Observable} from 'rxjs/Observable';
import {AppSettings} from '../appSettings';
import 'rxjs/add/operator/map';

@Injectable()
export class MessageService {

    constructor(private http: Http, 
                @Inject('ApiEndpoint') private apiEndpoint: string) { }

    getMessages(): Observable<Message[]> {
        return this.http.get(`${this.apiEndpoint}/messages`)
            .map(response => response.json())
            .map((messages: Object[]) => {
                return messages.map(message => this.parseData(message));
            });
    } 
    // the rest of the code...
}
30
Morteza Manavi

Voici mon expérience récente avec ce scénario:

  • @ angular/cli: 1.0.0
  • noeud: 6.10.2
  • @ angular/core: 4.0.0

J'ai suivi les documents officiels et mis à jour ici:

https://angular.io/docs/ts/latest/guide/dependency-injection.html#!#dependency-injection-tokens

Il semble que OpaqueToken soit maintenant obsolète et que nous devons utiliser InjectionToken. Ce sont donc mes fichiers qui fonctionnent comme un charme:

app-config.interface.ts

export interface IAppConfig {

  STORE_KEY: string;

}

app-config.constants.ts

import { InjectionToken } from "@angular/core";
import { IAppConfig } from "./app-config.interface";

export const APP_DI_CONFIG: IAppConfig = {

  STORE_KEY: 'l@_list@'

};

export let APP_CONFIG = new InjectionToken< IAppConfig >( 'app.config' );

app.module.ts

import { APP_CONFIG, APP_DI_CONFIG } from "./app-config/app-config.constants";

@NgModule( {
  declarations: [ ... ],
  imports: [ ... ],
  providers: [
    ...,
    {
      provide: APP_CONFIG,
      useValue: APP_DI_CONFIG
    }
  ],
  bootstrap: [ ... ]
} )
export class AppModule {}

my-service.service.ts

  constructor( ...,
               @Inject( APP_CONFIG ) private config: IAppConfig) {

    console.log("This is the App's Key: ", this.config.STORE_KEY);
    //> This is the App's Key:  l@_list@

  }

Le résultat est propre et il n'y a pas d'avertissement sur la console, merci au récent commentaire de John Papa dans ce numéro:

https://github.com/angular/angular-cli/issues/2034

La clé a été implémentée dans un fichier différent de l'interface.

28
JavierFuentes

Toutes les solutions semblent être compliquées. Je cherche la solution la plus simple pour ce cas et je veux juste utiliser des constantes. Les constantes sont simples. Y a-t-il quelque chose qui parle contre la solution suivante?

app.const.ts

'use strict';

export const dist = '../path/to/dist/';

app.service.ts

import * as AppConst from '../app.const'; 

@Injectable()
export class AppService {

    constructor (
    ) {
        console.log('dist path', AppConst.dist );
    }

}
13

Il suffit d'utiliser une constante de typeScript

export var API_ENDPOINT = 'http://127.0.0.1:6666/api/';

Vous pouvez l’utiliser dans l’injecteur de dépendance en utilisant

bootstrap(AppComponent, [provide(API_ENDPOINT, {useValue: 'http://127.0.0.1:6666/api/'}), ...]);
7
SnareChops

Si vous utilisez Webpack , ce que je recommande, vous pouvez configurer des constantes pour différents environnements. Cela est particulièrement utile lorsque vous avez différentes valeurs constantes par environnement.

Vous aurez probablement plusieurs fichiers webpack dans votre répertoire /config (par exemple, webpack.dev.js, webpack.prod.js, etc.). Ensuite, vous aurez un custom-typings.d.ts que vous les ajouterez ici. Voici le modèle général à suivre dans chaque fichier et un exemple d'utilisation dans un composant.

webpack. {env} .js

const API_URL = process.env.API_URL = 'http://localhost:3000/';
const JWT_TOKEN_NAME = "id_token";
...
    plugins: [
      // NOTE: when adding more properties, make sure you include them in custom-typings.d.ts
      new DefinePlugin({
        'API_URL': JSON.stringify(API_URL),
        'JWT_TOKEN_NAME': JSON.stringify(JWT_TOKEN_NAME)
      }),

custom-typings.d.ts

declare var API_URL: string;
declare var JWT_TOKEN_NAME: string;
interface GlobalEnvironment {
  API_URL: string;
  JWT_TOKEN_NAME: string;
}

Composant

export class HomeComponent implements OnInit {
  api_url:string = API_URL;
  authToken: string = "Bearer " + localStorage.getItem(JWT_TOKEN_NAME)});
}
4
occasl

Une approche pour Angular4 serait de définir une constante au niveau du module:

const api_endpoint = 'http://127.0.0.1:6666/api/';

@NgModule({
  declarations: [AppComponent],
  bootstrap: [AppComponent],
  providers: [
    MessageService,
    {provide: 'API_ENDPOINT', useValue: api_endpoint}
  ]
})
export class AppModule {
}

Ensuite, à votre service:

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

@Injectable()
export class MessageService {

    constructor(private http: Http, 
      @Inject('API_ENDPOINT') private api_endpoint: string) { }

    getMessages(): Observable<Message[]> {
        return this.http.get(this.api_endpoint+'/messages')
            .map(response => response.json())
            .map((messages: Object[]) => {
                return messages.map(message => this.parseData(message));
            });
    }

    private parseData(data): Message {
        return new Message(data);
    }
}
3
Juangui Jordán

L'utilisation d'un fichier de propriétés généré lors d'une construction est simple et facile. C'est l'approche utilisée par la CLI angulaire. Définissez un fichier de propriétés pour chaque environnement et utilisez une commande pendant la construction pour déterminer le fichier copié dans votre application. Ensuite, importez simplement le fichier de propriétés à utiliser.

https://github.com/angular/angular-cli#build-targets-and-environment-files

2
R.Creager

Le module.constant d'AngularJS ne définit pas une constante au sens standard.

Bien qu’il s’agisse d’un mécanisme d’enregistrement de fournisseur, il est préférable de le comprendre dans le contexte de la fonction module.value ($provide.value) associée. La documentation officielle indique clairement le cas d'utilisation:

Enregistrez un service de valeur avec l'injecteur $, tel qu'une chaîne, un nombre, un tableau, un objet ou une fonction. C'est l'abréviation d'enregistrement d'un service pour lequel la propriété $ get de son fournisseur est une fonction d'usine qui ne prend aucun argument et renvoie la valeur service. Cela signifie également qu'il n'est pas possible d'injecter d'autres services dans un service de valeur.

Comparez cela à la documentation de module.constant ($provide.constant), qui indique également clairement le cas d'utilisation (l'emphase mienne):

Enregistrez un service constant avec l'injecteur $, tel qu'une chaîne, un nombre, un tableau, un objet ou une fonction. Comme pour la valeur, il n’est pas possible d’injecter d’autres services dans une constante . Mais contrairement à value, une constante peut être injectée dans une fonction de configuration de module (voir angular.Module) et ne peut pas être annulée par un décorateur AngularJS.

Par conséquent, la fonction AngularJS constant ne fournit pas une constante dans la signification généralement comprise du terme sur le terrain.

Cela dit, les restrictions imposées à l'objet fourni, ainsi que sa disponibilité antérieure via l'injecteur $, suggèrent clairement que le nom est utilisé par analogie.

Si vous vouliez une constante réelle dans une application AngularJS, vous "en fourniriez" une de la même manière que vous le feriez dans n'importe quel programme JavaScript qui est:

export const π = 3.14159265;

Dans Angular 2, la même technique est applicable.

Les applications Angular 2 n'ont pas de phase de configuration au même sens que les applications AngularJS. De plus, il n’existe pas de mécanisme de décorateur de services ( AngularJS Decorator ), mais cela n’est pas particulièrement surprenant compte tenu de la différence qui existe entre eux.

L'exemple de

angular
  .module('mainApp.config', [])
  .constant('API_ENDPOINT', 'http://127.0.0.1:6666/api/');

est vaguement arbitraire et légèrement déroutant parce que $provide.constant est utilisé pour spécifier un objet qui est incidentally également une constante. Vous pourriez aussi bien avoir écrit

export const apiEndpoint = 'http://127.0.0.1:6666/api/';

pour tous les deux peuvent changer.

Maintenant, l’argument de la testabilité, se moquant de la constante, est diminué parce qu’il ne change pas littéralement.

On ne se moque pas de π.

Bien sûr, la sémantique de votre application peut indiquer que votre point de terminaison peut changer ou que votre API peut avoir un mécanisme de basculement non transparent. Il serait donc raisonnable que le point de terminaison de l'API change dans certaines circonstances.

Mais dans ce cas, le fournir sous forme de chaîne de caractères représentant une seule URL vers la fonction constant n'aurait pas fonctionné.

Un meilleur argument, et probablement plus cohérent avec la raison de l’existence de la fonction AngularJS $provide.constant, est que, lorsqu’on a introduit AngularJS, JavaScript n’avait pas de concept de module standard. Dans ce cas, les globals seraient utilisés pour partager des valeurs, mutables ou immuables, et l'utilisation de globals est problématique.

Cela dit, fournir quelque chose comme cela dans un cadre augmente le couplage à ce cadre. Il mélange également une logique spécifique angulaire avec une logique qui fonctionnerait dans tout autre système.

Cela ne veut pas dire que c’est une approche fausse ou nuisible, mais personnellement, si je veux une constante constante dans une application Angular 2, j’écrirai

export const π = 3.14159265;

comme je l'aurais si j'utilisais AngularJS.

Plus les choses changent...

0
Aluan Haddad

J'ai une autre façon de définir des constantes globales. Parce que si nous définissons dans le fichier ts, si nous construisons en mode production, il n’est pas facile de trouver des constantes pour modifier la valeur.

export class SettingService  {

  constructor(private http: HttpClient) {

  }

  public getJSON(file): Observable<any> {
      return this.http.get("./assets/configs/" + file + ".json");
  }
  public getSetting(){
      // use setting here
  }
}

Dans le dossier de l'application, j'ajoute le dossier configs/setting.json

Contenu dans setting.json

{
    "baseUrl": "http://localhost:52555"
}

Dans le module d'application, ajoutez APP_INITIALIZER

   {
      provide: APP_INITIALIZER,
      useFactory: (setting: SettingService) => function() {return setting.getSetting()},
      deps: [SettingService],
      multi: true
    }

de cette façon, je peux changer plus facilement la valeur du fichier json . Je l’utilise également pour les messages d’erreur/avertissement constants.

0
Hien Nguyen

Le meilleur moyen de créer des constantes à l'échelle de l'application dans Angular 2 consiste à utiliser les fichiers environment.ts. L'avantage de déclarer de telles constantes est que vous pouvez les faire varier en fonction de l'environnement car il peut exister un fichier d'environnement différent pour chaque environnement.

0
Hassan Arafat