web-dev-qa-db-fra.com

App.settings - la Angular façon?

Je veux ajouter une section App Settings dans mon application où elle contiendra des const et des valeurs prédéfinies.

J'ai déjà lu cette réponse qui utilise OpaqueToken mais il est déconseillé dans Angular. Cet article explique les différences, mais il ne fournit pas un exemple complet et mes tentatives échouent.

Voici ce que j'ai essayé (je ne sais pas si c'est la bonne façon):

//ServiceAppSettings.ts

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

const CONFIG = {
  apiUrl: 'http://my.api.com',
  theme: 'suicid-squad',
  title: 'My awesome app'
};
const FEATURE_ENABLED = true;
const API_URL = new InjectionToken<string>('apiUrl');

Et voici le composant où je veux utiliser ces consts:

//MainPage.ts

import {...} from '@angular/core'
import {ServiceTest} from "./ServiceTest"

@Component({
  selector: 'my-app',
  template: `
   <span>Hi</span>
  ` ,  providers: [
    {
      provide: ServiceTest,
      useFactory: ( apiUrl) => {
        // create data service
      },
      deps: [

        new Inject(API_URL)
      ]
    }
  ]
})
export class MainPage {


}

Mais ça ne marche pas et j'ai des erreurs.

Question:

Comment utiliser les valeurs "app.settings" de la manière Angular?

plunker

NB Bien sûr, je peux créer un service Injectable et le mettre dans le fournisseur du NgModule, mais comme je l’ai dit, je veux le faire avec InjectionToken, à la manière Angular.

59
Royi Namir

J'ai compris comment faire cela avec InjectionTokens (voir exemple ci-dessous), et si votre projet a été construit avec le Angular CLI, vous pouvez utiliser les fichiers d'environnement trouvés dans /environments pour static application wide settings comme un Point de terminaison de l'API, mais en fonction des exigences de votre projet, vous finirez probablement par utiliser les deux, car les fichiers d'environnement ne sont que des littéraux d'objet, tandis qu'une configuration injectable utilisant InjectionToken peut utiliser les variables d'environnement et, comme c'est une classe, une logique peut être appliquée configurez-le en fonction d'autres facteurs de l'application, tels que les données de demande http initiales, le sous-domaine, etc.

Exemple de jetons d'injection

/ app/app-config.module.ts

import { NgModule, InjectionToken } from '@angular/core';
import { environment } from '../environments/environment';

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

export class AppConfig {
  apiEndpoint: string;
}

export const APP_DI_CONFIG: AppConfig = {
  apiEndpoint: environment.apiEndpoint
};

@NgModule({
  providers: [{
    provide: APP_CONFIG,
    useValue: APP_DI_CONFIG
  }]
})
export class AppConfigModule { }

/ app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppConfigModule } from './app-config.module';

@NgModule({
  declarations: [
    // ...
  ],
  imports: [
    // ...
    AppConfigModule
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Maintenant, vous pouvez simplement le DI dans n'importe quel composant, service, etc.:

/ app/core/auth.service.ts

import { Injectable, Inject } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';

import { APP_CONFIG, AppConfig } from '../app-config.module';
import { AuthHttp } from 'angular2-jwt';

@Injectable()
export class AuthService {

  constructor(
    private http: Http,
    private router: Router,
    private authHttp: AuthHttp,
    @Inject(APP_CONFIG) private config: AppConfig
  ) { }

  /**
   * Logs a user into the application.
   * @param payload
   */
  public login(payload: { username: string, password: string }) {
    return this.http
      .post(`${this.config.apiEndpoint}/login`, payload)
      .map((response: Response) => {
        const token = response.json().token;
        sessionStorage.setItem('token', token); // TODO: can this be done else where? interceptor
        return this.handleResponse(response); // TODO:  unset token shouldn't return the token to login
      })
      .catch(this.handleError);
  }

  // ...
}

Vous pouvez également taper vérifier la configuration à l'aide de la AppConfig exportée.

43
mtpultz

Si vous utilisez angular-cli , il existe une autre option:

La CLI angulaire fournit des fichiers d’environnement dans src/environments (les valeurs par défaut sont environment.ts (dev) et environment.prod.ts (production)).

Notez que vous devez fournir les paramètres de configuration dans tous les fichiers environment.*, par exemple,

environment.ts :

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

environment.prod.ts :

export const environment = {
  production: true,
  apiEndpoint: '__your_production_server__'
};

et les utiliser dans votre service (le fichier d’environnement correct est choisi automatiquement):

api.service.ts

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

@Injectable()
export class ApiService {     

  public apiRequest(): Observable<MyObject[]> {
    const path = environment.apiEndpoint + `/objects`;
    // ...
  }

// ...
}

En savoir plus sur les environnements d’application sur Github (version 6 du CLI angulaire) ou dans le guide officiel Angular (version 7) .

125
tilo

Il est déconseillé d'utiliser les fichiers environment.*.ts pour la configuration de votre adresse URL d'API. Il semble que vous devriez le faire car cela mentionne le mot "environnement".

Utiliser ceci est en fait configuration à la compilation. Si vous souhaitez modifier l'URL de l'API, vous devrez le reconstruire. C'est quelque chose que vous ne voulez pas avoir à faire ... il suffit de demander à votre département QA amical :)

Ce dont vous avez besoin, c’est configuration d’exécution, c’est-à-dire que l’application charge sa configuration au démarrage.

Quelques autres réponses abordent ce sujet, mais la différence est que la configuration doit être chargée dès le démarrage de l'application , de sorte qu'elle puisse être utilisée par un service normal quand il en a besoin.

Pour implémenter la configuration d'exécution:

  1. Ajouter un fichier de configuration JSON au dossier /src/assets/ (copié lors de la construction)
  2. Créer un AppConfigService pour charger et distribuer la configuration
  3. Chargez la configuration en utilisant un APP_INITIALISER

1. Ajouter un fichier de configuration à /src/assets

Vous pouvez l'ajouter à un autre dossier, mais vous devez indiquer à la CLI qu'il s'agit d'un actif dans le angular.json. Commencez par utiliser le dossier des actifs:

{
  "apiBaseUrl": "https://development.local/apiUrl"
}

2. Créez AppConfigService

C'est le service qui sera injecté chaque fois que vous aurez besoin de la valeur de configuration:

@Injectable({
  providedIn: 'root'
})
export class AppConfigService {

  private appConfig: any;

  constructor(private http: HttpClient) { }

  loadAppConfig() {
    return this.http.get('/assets/config.json')
      .toPromise()
      .then(data => {
        this.appConfig = data;
      });
  }

  // This is an example property ... you can make it however you want.
  get apiBaseUrl() {

    if (!this.appConfig) {
      throw Error('Config file not loaded!');
    }

    return this.appConfig.apiBaseUrl;
  }
}

3. Chargez la configuration à l'aide d'un APP_INITIALISER

Pour permettre à la AppConfigService d'être injectée en toute sécurité, avec la configuration entièrement chargée, nous devons charger la configuration au moment du démarrage de l'application. Il est important de noter que la fonction d’usine d’initialisation doit renvoyer un Promise pour que Angular sache attendre la fin de la résolution avant la fin du démarrage:

NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [
    {
      provide: APP_INITIALIZER,
      multi: true,
      deps: [AppConfigService],
      useFactory: (appConfigService: AppConfigService) => {
        return () => {
          //Make sure to return a promise!
          return appConfigService.loadAppConfig();
        };
      }
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Maintenant, vous pouvez l’injecter où vous le souhaitez et toute la configuration sera prête à être lue:

@Component({
  selector: 'app-test',
  templateUrl: './test.component.html',
  styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit {

  apiBaseUrl: string;

  constructor(private appConfigService: AppConfigService) {}

  ngOnInit(): void {
    this.apiBaseUrl = this.appConfigService.apiBaseUrl;
  }

}

Je ne saurais trop le dire, en configurant votre Les URL des API car la configuration à la compilation est un anti-motif. Utilisez la configuration d'exécution.

22
Matt Tester

Voici ma solution, charge à partir de .json pour autoriser les modifications sans reconstruire

import { Injectable, Inject } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { Location } from '@angular/common';

@Injectable()
export class ConfigService {

    private config: any;

    constructor(private location: Location, private http: Http) {
    }

    async apiUrl(): Promise<string> {
        let conf = await this.getConfig();
        return Promise.resolve(conf.apiUrl);
    }

    private async getConfig(): Promise<any> {
        if (!this.config) {
            this.config = (await this.http.get(this.location.prepareExternalUrl('/assets/config.json')).toPromise()).json();
        }
        return Promise.resolve(this.config);
    }
}

et config.json

{
    "apiUrl": "http://localhost:3000/api"
}
7
PJM

Voici mes deux solutions pour cela

1. Stocker dans des fichiers JSON

Créez simplement un fichier json et insérez votre composant dans la méthode $http.get(). Si j’avais besoin de ce niveau très bas, c’est bon et rapide.

2. Stocker en utilisant des services de données

Si vous souhaitez stocker et utiliser tous les composants ou avoir une utilisation importante, il est préférable d'utiliser le service de données. Comme ça :

  1. Créez simplement un dossier statique dans le dossier src/app.

  2. Créez un fichier nommé fuels.ts dans un dossier statique. Vous pouvez également stocker d'autres fichiers statiques ici. Laissez définir vos données comme ceci. En supposant que vous ayez des données sur les carburants.

__

export const Fuels {

   Fuel: [
    { "id": 1, "type": "A" },
    { "id": 2, "type": "B" },
    { "id": 3, "type": "C" },
    { "id": 4, "type": "D" },
   ];
   }
  1. Créez un nom de fichier static.services.ts

__

import { Injectable } from "@angular/core";
import { Fuels } from "./static/fuels";

@Injectable()
export class StaticService {

  constructor() { }

  getFuelData(): Fuels[] {
    return Fuels;
  }
 }`
  1. Maintenant, vous pouvez le rendre disponible pour chaque module

importez simplement dans le fichier app.module.ts comme ceci et changez de fournisseur

import { StaticService } from './static.services';

providers: [StaticService]

Maintenant, utilisez ceci comme StaticService dans n’importe quel module.

C'est tout.

1
amku91