App.settings-角度のある方法?


87

追加したい App Settingsいくつかの定数と事前定義された値を含むセクションをアプリます。

を使用するこの回答はすでに読んでいますOpaqueTokenが、Angularでは非推奨です。この記事では違いについて説明しますが、完全な例は提供されておらず、私の試みは失敗しました。

これが私が試したことです(それが正しい方法かどうかはわかりません):

//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');

そして、これは私がそれらの定数を使用したいコンポーネントです:

//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 {


}

しかし、それは機能せず、エラーが発生します。

質問:

「app.settings」値をAngularの方法で使用するにはどうすればよいですか?

プランカー

NB確かに、Injectableサービスを作成して、NgModuleのプロバイダーに配置することはできますが、私が言ったInjectionTokenように、Angularの方法でそれを実行したいと思います。


現在の公式ドキュメントに基づいて、ここで私の答えを確認できます
JavierFuentes 2017年

@javierいいえ。2つのプロバイダーが同じ名前を提供している場合、リンクに問題があるため、問題が発生します。不透明なトークンの入力
Royi

[OpaqueTokenは非推奨です]。(angular.io/api/core/OpaqueTokenこの記事では、Angularプロバイダーでの名前の衝突を防ぐ方法について説明しています
JavierFuentes 2017年

Yaeh私は知っていますが、それでもリンクされた記事は間違っています。
Royi Namir 2017

2
リンクを下回ることもして、角度設定スキーマの新しいアーキテクチャを使用するのが好き一人一人のために役立つことができdevblogs.microsoft.com/premier-developer/...を
M_Farahmand

回答:


56

私はInjectionTokensを使用してこれを行う方法を理解しました(以下の例を参照)。プロジェクトがを使用して構築されたAngular CLI場合、APIエンドポイントのよう/environmentsに静的用にある環境ファイルを使用できますがapplication wide settings、プロジェクトの要件によっては、最終的には環境ファイルは単なるオブジェクトリテラルであるため、両方を使用します。一方、を使用した注入可能な構成でInjectionTokenは環境変数を使用でき、クラスであるため、初期httpリクエストデータ、サブドメインなど、アプリケーションの他の要素に基づいて構成するロジックを適用できます。 、など。

インジェクショントークンの例

/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 { }

これで、任意のコンポーネント、サービスなどにDIすることができます。

/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);
  }

  // ...
}

次に、エクスポートされたAppConfigを使用して構成をタイプチェックすることもできます。


いいえ。ただし、最初の部分を文字通りコピーしてファイルに貼り付け、app.module.tsファイルにインポートし、DIを任意の場所にインポートして、コンソールに出力することができます。プランカーでこれを設定するのに時間がかかり、それからそれらのステップを実行します。
mtpultz 2017

ああ、私はあなたがすでにこれのためのプランカーを持っていると思いました:-)ありがとう。
Royi Namir 2017

欲しい人のために:plnkr.co/edit/2YMZk5mhP1B4jTgA37B8
p =

1
AppConfigインターフェイス/クラスをエクスポートする必要はないと思います。DIを行うときに絶対に使用する必要はありません。これを1つのファイルで機能させるには、インターフェイスではなくクラスである必要がありましたが、それは問題ではありません。実際、スタイルガイドでは、コードが少なくて済み、それらを使用して型チェックを実行できるため、インターフェイスの代わりにクラスを使用することを推奨しています。ジェネリックを介したInjectionTokenによる使用に関しては、含める必要があります。
mtpultz 2017

1
Azureの環境変数とJSON変換機能を使用してAPIエンドポイントを動的に挿入しようとしていますが、この回答は環境ファイルからapiEndpointを取得しているようです。構成からどのように取得してエクスポートしますか?
アーチボルド

138

使用している場合 、さらに別のオプションがあります:

Angular CLIは、環境ファイルを提供しますsrc/environments(デフォルトのファイルはenvironment.ts(dev)とenvironment.prod.ts(production)です)。

すべてのenvironment.*ファイルで構成パラメーターを指定する必要があることに注意してください。

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__'
};

そしてそれらをサービスで使用します(正しい環境ファイルが自動的に選択されます):

api.service.ts

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

@Injectable()
export class ApiService {     

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

// ...
}

Github(Angular CLIバージョン6)または公式のAngularガイド(バージョン7)のアプリケーション環境の詳細をご覧ください。


2
ビルドを移動している間、バンドルとしても変更されます。本番環境に移行した後、コードではなくサーブの構成を変更する必要があります
kamalav 2018年

42
これは、通常のソフトウェア開発では多少アンチパターンです。APIURLは単なる構成です。別の環境用にアプリを再構成するために再構築する必要はありません。一度ビルドし、何度もデプロイする必要があります(プリプロダクション、ステージング、プロッドなど)。
マットテスター

3
@MattTesterこれは実際には現在公式のAngular-CLIストーリーです。この質問に対するより良い答えがある場合は、遠慮なく投稿してください。
tilo 2018

7
ビルド後に構成できますか?
NK

1
ああ、私はコメントを読み間違えました。これがアンチパターンに役立つことに同意します。動的なランタイム構成の話があると思いました。
イェンスボーダル

83

APIURLenvironment.*.ts構成にファイルを使用することはお勧めできません。これは「環境」という言葉に言及しているので、あなたはそうすべきだと思われます。

これを使用するのは、実際にはコンパイル時の構成です。API URLを変更する場合は、再構築する必要があります。それはあなたがしたくないことです...あなたのフレンドリーなQA部門に聞いてください:)

必要なのはランタイム構成です。つまり、アプリは起動時に構成をロードします。

他のいくつかの回答はこれに触れていますが、違いは構成をロードする必要があるということです 、アプリの起動ため、通常のサービスが必要なときにいつでも使用できるようにすることです。

ランタイム構成を実装するには:

  1. JSON構成ファイルをに追加します /src/assets/フォルダーにます(ビルド時にコピーされます)
  2. 作成する AppConfigServiceをロードして配布ためのを
  3. を使用して構成をロードします APP_INITIALIZER

1.構成ファイルをに追加します /src/assets

別のフォルダーに追加することもできますが、CLIにそれがのアセットであることを通知する必要がありますangular.json。アセットフォルダーの使用を開始します。

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

2.作成する AppConfigService

これは、構成値が必要なときにいつでも注入されるサービスです。

@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.を使用して構成をロードします APP_INITIALIZER

構成をAppConfigService完全にロードした状態でを安全に挿入できるようにするには、アプリの起動時に構成をロードする必要があります。重要なことに、初期化ファクトリ関数はを返す必要がありますPromiseため、Angularは解決が完了するまで待機してから起動を終了することを認識します。

@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 { }

これで、必要な場所に挿入でき、すべての構成を読み取る準備が整います。

@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;
  }

}

十分に強く言うことはできません。コンパイル時の構成としてAPIURLを構成することは、アンチパターンです。ランタイム構成を使用します。


4
ローカルファイルまたは別のサービス、コンパイル時の構成をAPIURLに使用しないでください。アプリが製品(インストールする購入者)として販売されている場合、アプリをコンパイルしたくない場合などを想像してみてください。いずれにしても、2年前に作成されたものを再コンパイルしたくないという理由は、 APIURLが変更されました。リスク!!
マットテスター

1
@Bloodhound複数持つことができAPP_INITIALIZERますが、簡単に相互に依存させることはできないと思います。良い質問があるようですので、ここにリンクしてください。
マットテスター

2
@MattTester -もし角度が今までそれが私たちの問題を解決するこの機能を実装していますgithub.com/angular/angular/issues/23279#issuecomment-528417026
マイクBecatti

2
@CrhistianRamirezこれはアプリの観点からです。構成は実行時まで不明であり、静的ファイルはビルドの外部にあり、デプロイ時にさまざまな方法で設定できます。静的ファイルは、機密性の低い構成には問題ありません。同じ手法でAPIまたはその他の保護されたエンドポイントが可能ですが、認証して保護する方法が次の課題です。
マットテスター

1
@DaleK行間を読んで、WebDeployを使用してデプロイしています。Azure DevOpsなどのデプロイメントパイプラインを使用している場合は、次のステップとして構成ファイルを正しく設定することができます。構成の設定は、デプロイメントプロセス/パイプラインの責任であり、デフォルトの構成ファイルの値を上書きできます。それが明らかになることを願っています。
マットテスター

8

これが私の解決策です。.jsonからロードして、再構築せずに変更できるようにします

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);
    }
}

およびconfig.json

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

1
このアプローチの問題は、config.jsonが世界中に公開されていることです。誰かがwww.mywebsite.com/assetts/config.jsonと入力するのをどのように防ぎますか?
アルベルトL.ボンフィリオ2018年

1
@ AlbertoL.Bonfiglio外部からconfig.jsonファイルへのアクセスを許可しないようにサーバーを構成します(またはパブリックアクセスのないディレクトリに配置します)
Alex Pandrea 2018年

これも私のお気に入りのソリューションですが、それでもセキュリティリスクを懸念しています。
Viqas

7
どうか、私がそれを正しくするのを手伝ってくれませんか?角度のある環境では、従来よりもどのようにリスクが高くなりますか?environments.prod.tsafterの完全なコンテンツは、ある時点でいくつng build --prodかの.jsファイルにあります。難読化されている場合でも、からのデータenvironments.prod.tsはクリアテキストになります。そして、すべての.jsファイルと同様に、エンドユーザーのマシンで利用できるようになります。
igann

5
@ AlbertoL.Bonfiglio Angularアプリは本質的にクライアントアプリケーションであり、データと構成の受け渡しにはJavaScriptが使用されるため、秘密の構成を使用しないでください。すべてのシークレット構成定義は、ユーザーのブラウザーまたはブラウザーツールがアクセスできないAPIレイヤーの背後にある必要があります。APIのベースURIのような値は、APIがユーザーのログイン(https経由のベアラートークン)に基づいて独自の資格情報とセキュリティを持っている必要があるため、一般の人がアクセスできます。
トミーエリオット

4

貧乏人の設定ファイル:

bodyタグの最初のラインとしてindex.htmlに追加します。

<script lang="javascript" src="assets/config.js"></script>

Assets / config.jsを追加します:

var config = {
    apiBaseUrl: "http://localhost:8080"
}

config.tsを追加します。

export const config: AppConfig = window['config']

export interface AppConfig {
    apiBaseUrl: string
}

真剣に、ソリューションを最も基本的なコンポーネントに煮詰め、タイプの一貫性を維持するための+1。
明るい

4

APP_INITIALIZER他のサービスプロバイダーが構成を挿入する必要がある状況では、これにforを使用しても機能しないことがわかりました。APP_INITIALIZER実行する前にインスタンス化できます。

ルートモジュールをブートストラップfetchするplatformBrowserDynamic()前に、config.jsonファイルを読み取り、パラメーターにインジェクショントークンを使用してそれを提供するために使用する他のソリューションを見てきました。ただしfetch、すべてのブラウザー、特にターゲットとするモバイルデバイスのWebViewブラウザーでサポートされているわけではありません。

以下は、PWAとモバイルデバイス(WebView)の両方で機能するソリューションです。注:これまでのところ、Androidでのみテストしています。在宅勤務とは、ビルドするMacにアクセスできないことを意味します。

main.ts

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { APP_CONFIG } from './app/lib/angular/injection-tokens';

function configListener() {
  try {
    const configuration = JSON.parse(this.responseText);

    // pass config to bootstrap process using an injection token
    platformBrowserDynamic([
      { provide: APP_CONFIG, useValue: configuration }
    ])
      .bootstrapModule(AppModule)
      .catch(err => console.error(err));

  } catch (error) {
    console.error(error);
  }
}

function configFailed(evt) {
  console.error('Error: retrieving config.json');
}

if (environment.production) {
  enableProdMode();
}

const request = new XMLHttpRequest();
request.addEventListener('load', configListener);
request.addEventListener('error', configFailed);
request.open('GET', './assets/config/config.json');
request.send();

このコード:

  1. config.jsonファイルの非同期リクエストを開始します。
  2. リクエストが完了すると、JSONを解析してJavascriptオブジェクトにします
  3. APP_CONFIGブートストラップの前に、インジェクショントークンを使用して値を提供します。
  4. そして最後にルートモジュールをブートストラップします。

APP_CONFIGその後、の追加プロバイダーに注入でき、app-module.ts定義されます。たとえば、次の方法でFIREBASE_OPTIONSインジェクショントークンを初期化でき@angular/fireます。

{
      provide: FIREBASE_OPTIONS,
      useFactory: (config: IConfig) => config.firebaseConfig,
      deps: [APP_CONFIG]
}

非常に一般的な要件に対して、このすべてを行うのは驚くほど難しい(そしてハッキーな)ことだと思います。近い将来、非同期プロバイダーファクトリのサポートなどのより良い方法があることを願っています。

完全を期すための残りのコード...

app/lib/angular/injection-tokens.ts

import { InjectionToken } from '@angular/core';
import { IConfig } from '../config/config';

export const APP_CONFIG = new InjectionToken<IConfig>('app-config');

そしてapp/lib/config/config.ts、JSON構成ファイルのインターフェースを定義します。

export interface IConfig {
    name: string;
    version: string;
    instance: string;
    firebaseConfig: {
        apiKey: string;
        // etc
    }
}

構成は次の場所に保存されassets/config/config.jsonます:

{
  "name": "my-app",
  "version": "#{Build.BuildNumber}#",
  "instance": "localdev",
  "firebaseConfig": {
    "apiKey": "abcd"
    ...
  }
}

注:Azure DevOpsタスクを使用してBuild.BuildNumberを挿入し、デプロイ時にさまざまなデプロイメント環境を他の設定に置き換えます。


2

これが私の2つの解決策です

1.jsonファイルに保存します

jsonファイルを作成し、$http.get()メソッドでコンポーネントを取得するだけです。私がこれを非常に低くする必要があったなら、それは良くて速いです。

2.データサービスを使用して保存する

すべてのコンポーネントに保存して使用する場合、または使用量が多い場合は、データサービスを使用することをお勧めします。このような :

  1. フォルダ内に静的フォルダを作成するだけsrc/appです。

  2. 次の名前のファイルを作成します fuels.tsを静的フォルダーにます。他の静的ファイルもここに保存できます。このようにデータを定義しましょう。あなたが燃料データを持っていると仮定します。

__

export const Fuels {

   Fuel: [
    { "id": 1, "type": "A" },
    { "id": 2, "type": "B" },
    { "id": 3, "type": "C" },
    { "id": 4, "type": "D" },
   ];
   }
  1. ファイル名static.services.tsを作成します

__

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

@Injectable()
export class StaticService {

  constructor() { }

  getFuelData(): Fuels[] {
    return Fuels;
  }
 }`
  1. これをすべてのモジュールで利用できるようになりました

このようにapp.module.tsファイルにインポートしてプロバイダーを変更するだけです

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

providers: [StaticService]

今これをとして使用してください StaticServiceを他のモジュールとにます。

それで全部です。


再コンパイルする必要がないので、良い解決策です。環境は、コードにハードコーディングするようなものです。不快な。+1
Terrance 0019年


0

私が参加する何年も前にこの問題が発生し、ユーザーと環境の情報にローカルストレージを使用するソリューションを導入しました。正確には角度1。0日。以前は、実行時にjsファイルを動的に作成し、生成されたapiURLをグローバル変数に配置していました。最近はもう少しOOP主導で、ローカルストレージを何にも使用していません。

環境の決定とAPIURLの作成の両方に適したソリューションを作成しました。

これはどう違うのですか?

config.jsonファイルが読み込まれない限り、アプリは読み込まれません。ファクトリ関数を使用して、より高度なSOCを作成します。これをサービスにカプセル化することはできますが、ファイルの異なるセクション間の唯一の類似点がファイル内に一緒に存在することである場合、理由はわかりませんでした。ファクトリ関数があると、関数を受け入れることができる場合、その関数をモジュールに直接渡すことができます。最後に、ファクトリ関数を使用できる場合は、InjectionTokenを簡単に設定できます。

欠点?

構成するモジュールでファクトリ関数をforRoot()またはforChild()に渡すことが許可されておらず、他に方法がない場合は、このセットアップ(および他のほとんどの回答)を使用できません。ファクトリ関数を使用してパッケージを構成します。

指示

  1. フェッチを使用してjsonファイルを取得し、オブジェクトをウィンドウに保存してカスタムイベントを発生させます。--IEとの互換性のために、whatwg-fetchをインストールしてpolyfills.tsに追加することを忘れないでください
  2. イベントリスナーにカスタムイベントをリッスンさせます。
  3. イベントリスナーはイベントを受信し、ウィンドウからオブジェクトを取得してオブザーバブルに渡し、ウィンドウに保存されていたものをクリアします。
  4. ブートストラップAngular

-これが私のソリューションが本当に異なり始めるところです-

  1. 構造がconfig.jsonを表すインターフェイスをエクスポートするファイルを作成します。これは型の一貫性に非常に役立ちます。コードの次のセクションでは型が必要です。指定しないでください。{}またはany、より具体的なものを指定できることがわかっている場合は、
  2. 手順3で解析されたjsonファイルを渡すBehaviorSubjectを作成します。
  3. ファクトリ関数を使用して、構成のさまざまなセクションを参照し、SOCを維持します
  4. ファクトリ関数の結果を必要とするプロバイダーのInjectionTokenを作成します

-および/または-

  1. ファクトリ関数を、forRoot()メソッドまたはforChild()メソッドのいずれかで関数を受け入れることができるモジュールに直接渡します。

--main.ts

イベントリスナーを作成する前にwindow ["environment"]が入力されていないことを確認し、main.tsのコードが実行される前にwindow ["environment"]が他の方法で入力されるソリューションの可能性を許可します。

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { configurationSubject } from './app/utils/environment-resolver';

var configurationLoadedEvent = document.createEvent('Event');
configurationLoadedEvent.initEvent('config-set', true, true);
fetch("../../assets/config.json")
.then(result => { return result.json(); })
.then(data => {
  window["environment"] = data;
  document.dispatchEvent(configurationLoadedEvent);
}, error => window.location.reload());

/*
  angular-cli only loads the first thing it finds it needs a dependency under /app in main.ts when under local scope. 
  Make AppModule the first dependency it needs and the rest are done for ya. Event listeners are 
  ran at a higher level of scope bypassing the behavior of not loading AppModule when the 
  configurationSubject is referenced before calling platformBrowserDynamic().bootstrapModule(AppModule)

  example: this will not work because configurationSubject is the first dependency the compiler realizes that lives under 
  app and will ONLY load that dependency, making AppModule an empty object.

  if(window["environment"])
  {
    if (window["environment"].production) {
      enableProdMode();
    }
    configurationSubject.next(window["environment"]);
    platformBrowserDynamic().bootstrapModule(AppModule)
    .catch(err => console.log(err));
  }
*/
if(!window["environment"]) {
  document.addEventListener('config-set', function(e){
    if (window["environment"].production) {
      enableProdMode();
    }
    configurationSubject.next(window["environment"]);
    window["environment"] = undefined;
    platformBrowserDynamic().bootstrapModule(AppModule)
    .catch(err => console.log(err));
  });
}

--- environment-resolvers.ts

冗長性のためにwindow ["environment"]を使用してBehaviorSubjectに値を割り当てます。main.tsのコードを含め、Angularのアプリコードのいずれかが実行されるまでに、構成が既にプリロードされており、window ["environment"]がすでに入力されているソリューションを考案できます。

import { BehaviorSubject } from "rxjs";
import { IConfig } from "../config.interface";

const config = <IConfig>Object.assign({}, window["environment"]);
export const configurationSubject = new BehaviorSubject<IConfig>(config);
export function resolveEnvironment() {
  const env = configurationSubject.getValue().environment;
  let resolvedEnvironment = "";
  switch (env) {
 // case statements for determining whether this is dev, test, stage, or prod
  }
  return resolvedEnvironment;
}

export function resolveNgxLoggerConfig() {
  return configurationSubject.getValue().logging;
}

--app.module.ts-理解しやすいように削除

楽しい事実!古いバージョンのNGXLoggerでは、オブジェクトをLoggerModule.forRoot()に渡す必要がありました。実際、LoggerModuleはまだそうです!NGXLoggerは、セットアップにファクトリ関数を使用できるようにオーバーライドできるLoggerConfigを親切に公開します。

import { resolveEnvironment, resolveNgxLoggerConfig, resolveSomethingElse } from './environment-resolvers';
import { LoggerConfig } from 'ngx-logger';
@NgModule({
    modules: [
        SomeModule.forRoot(resolveSomethingElse)
    ],
    providers:[
        {
            provide: ENVIRONMENT,
            useFactory: resolveEnvironment
        },
        { 
            provide: LoggerConfig,
            useFactory: resolveNgxLoggerConfig
        }
    ]
})
export class AppModule

補遺

API URLの作成をどのように解決しましたか?

私は、コメントを介して各URLが何をするのかを理解できるようにしたかったし、タイプチェックが必要でした。これは、JavaScript(IMO)と比較してTypeScriptの最大の強みだからです。また、他の開発者が新しいエンドポイントを追加するためのエクスペリエンスと、可能な限りシームレスなAPIを作成したいと思いました。

環境(dev、test、stage、prod、 ""など)を取り込むクラスを作成し、この値を一連のクラス[1-N]に渡しました。このクラスの仕事は、各APIコレクションのベースURLを作成することです。 。各ApiCollectionは、APIの各コレクションのベースURLを作成する責任があります。独自のAPI、ベンダーのAPI、または外部リンクである可能性があります。そのクラスは、作成されたベースURLをそれが含む後続の各APIに渡します。以下のコードを読んで、必要最低限​​の例を確認してください。セットアップが完了すると、別の開発者が他に何も触れることなく、別のエンドポイントをApiクラスに追加するのは非常に簡単です。

TLDR; メモリ最適化のための基本的なOOPの原則と怠惰なゲッター

@Injectable({
    providedIn: 'root'
})
export class ApiConfig {
    public apis: Apis;

    constructor(@Inject(ENVIRONMENT) private environment: string) {
        this.apis = new Apis(environment);
    }
}

export class Apis {
    readonly microservices: MicroserviceApiCollection;

    constructor(environment: string) {
        this.microservices = new MicroserviceApiCollection(environment);
    }
}

export abstract class ApiCollection {
  protected domain: any;

  constructor(environment: string) {
      const domain = this.resolveDomain(environment);
      Object.defineProperty(ApiCollection.prototype, 'domain', {
          get() {
              Object.defineProperty(this, 'domain', { value: domain });
              return this.domain;
          },
          configurable: true
      });
  }
}

export class MicroserviceApiCollection extends ApiCollection {
  public member: MemberApi;

  constructor(environment) {
      super(environment);
      this.member = new MemberApi(this.domain);
  }

  resolveDomain(environment: string): string {
      return `https://subdomain${environment}.actualdomain.com/`;
  }
}

export class Api {
  readonly base: any;

  constructor(baseUrl: string) {
      Object.defineProperty(this, 'base', {
          get() {
              Object.defineProperty(this, 'base',
              { value: baseUrl, configurable: true});
              return this.base;
          },
          enumerable: false,
          configurable: true
      });
  }

  attachProperty(name: string, value: any, enumerable?: boolean) {
      Object.defineProperty(this, name,
      { value, writable: false, configurable: true, enumerable: enumerable || true });
  }
}

export class MemberApi extends Api {

  /**
  * This comment will show up when referencing this.apiConfig.apis.microservices.member.memberInfo
  */
  get MemberInfo() {
    this.attachProperty("MemberInfo", `${this.base}basic-info`);
    return this.MemberInfo;
  }

  constructor(baseUrl: string) {
    super(baseUrl + "member/api/");
  }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.