私が参加する何年も前にこの問題が発生し、ユーザーと環境の情報にローカルストレージを使用するソリューションを導入しました。正確には角度1。0日。以前は、実行時にjsファイルを動的に作成し、生成されたapiURLをグローバル変数に配置していました。最近はもう少しOOP主導で、ローカルストレージを何にも使用していません。
環境の決定とAPIURLの作成の両方に適したソリューションを作成しました。
これはどう違うのですか?
config.jsonファイルが読み込まれない限り、アプリは読み込まれません。ファクトリ関数を使用して、より高度なSOCを作成します。これをサービスにカプセル化することはできますが、ファイルの異なるセクション間の唯一の類似点がファイル内に一緒に存在することである場合、理由はわかりませんでした。ファクトリ関数があると、関数を受け入れることができる場合、その関数をモジュールに直接渡すことができます。最後に、ファクトリ関数を使用できる場合は、InjectionTokenを簡単に設定できます。
欠点?
構成するモジュールでファクトリ関数をforRoot()またはforChild()に渡すことが許可されておらず、他に方法がない場合は、このセットアップ(および他のほとんどの回答)を使用できません。ファクトリ関数を使用してパッケージを構成します。
指示
- フェッチを使用してjsonファイルを取得し、オブジェクトをウィンドウに保存してカスタムイベントを発生させます。--IEとの互換性のために、whatwg-fetchをインストールしてpolyfills.tsに追加することを忘れないでください
- イベントリスナーにカスタムイベントをリッスンさせます。
- イベントリスナーはイベントを受信し、ウィンドウからオブジェクトを取得してオブザーバブルに渡し、ウィンドウに保存されていたものをクリアします。
- ブートストラップAngular
-これが私のソリューションが本当に異なり始めるところです-
- 構造がconfig.jsonを表すインターフェイスをエクスポートするファイルを作成します。これは型の一貫性に非常に役立ちます。コードの次のセクションでは型が必要です。指定しないでください。
{}またはany、より具体的なものを指定できることがわかっている場合は、
- 手順3で解析されたjsonファイルを渡すBehaviorSubjectを作成します。
- ファクトリ関数を使用して、構成のさまざまなセクションを参照し、SOCを維持します
- ファクトリ関数の結果を必要とするプロバイダーのInjectionTokenを作成します
-および/または-
- ファクトリ関数を、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());
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 {
get MemberInfo() {
this.attachProperty("MemberInfo", `${this.base}basic-info`);
return this.MemberInfo;
}
constructor(baseUrl: string) {
super(baseUrl + "member/api/");
}
}