제가 가입하기 몇 년 전에이 문제가 발생했으며 사용자 및 환경 정보에 로컬 스토리지를 사용하는 솔루션을 마련했습니다. 정확한 각도는 1.0 일입니다. 이전에는 런타임에 js 파일을 동적으로 생성 한 다음 생성 된 api URL을 전역 변수에 배치했습니다. 요즘 우리는 OOP를 조금 더 많이 사용하며 로컬 스토리지를 사용하지 않습니다.
환경 결정과 API URL 생성을위한 더 나은 솔루션을 만들었습니다.
이것이 어떻게 다른가요?
config.json 파일이로드되지 않으면 앱이로드되지 않습니다. 공장 기능을 사용하여 더 높은 수준의 SOC를 생성합니다. 나는 이것을 서비스로 캡슐화 할 수 있었지만, 파일의 다른 섹션 사이의 유일한 유사점이 파일에 함께 존재한다는 것 뿐인 이유를 전혀 보지 못했습니다. 팩토리 함수가 있으면 함수를받을 수있는 경우 함수를 모듈에 직접 전달할 수 있습니다. 마지막으로, 공장 기능을 사용할 수있을 때 InjectionTokens를 설정하는 것이 더 쉽습니다.
단점?
구성하려는 모듈이 공장 함수를 forRoot () 또는 forChild ()로 전달하는 것을 허용하지 않고 다른 방법이 없다면이 설정 (및 대부분의 다른 답변)을 사용하면 운이 좋지 않습니다. 공장 기능을 사용하여 패키지를 구성하십시오.
명령
- fetch를 사용하여 json 파일을 검색하고 개체를 창에 저장하고 사용자 지정 이벤트를 발생시킵니다. -IE 호환성을 위해 whatwg-fetch를 설치하고 polyfills.ts에 추가하는 것을 잊지 마십시오.
- 사용자 정의 이벤트를 수신하는 이벤트 리스너가 있습니다.
- 이벤트 리스너는 이벤트를 수신하고 창에서 객체를 검색하여 관찰 가능 항목에 전달하고 창에 저장된 내용을 지 웁니다.
- 부트 스트랩 각도
-이것이 내 솔루션이 실제로 달라지기 시작하는 곳입니다-
- 구조 당신의 config.json를 나타내는 인터페이스를 내보내기 파일을 생성 - 정말 유형의 일관성에 도움이 코드의 다음 섹션은 유형을 필요로하고, 지정하지 않은
{}
또는 any
당신이 알고있을 때 당신이 뭔가 더 구체적인를 지정할 수 있습니다
- 3 단계에서 구문 분석 된 json 파일을 전달할 BehaviorSubject를 만듭니다.
- 공장 기능을 사용하여 구성의 다른 섹션을 참조하여 SOC를 유지합니다.
- 공장 함수의 결과를 필요로하는 공급자를위한 InjectionToken 생성
-및 / 또는-
- forRoot () 또는 forChild () 메서드에서 함수를받을 수있는 모듈에 팩토리 함수를 직접 전달합니다.
-main.ts
main.ts의 코드가 실행되기 전에 window [ "environment"]가 다른 수단으로 채워지는 솔루션의 가능성을 허용하기 위해 이벤트 리스너를 만들기 전에 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));
});
}
--- 환경 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이 주석을 통해 무엇을했는지 이해할 수 있기를 원했고 자바 스크립트 (IMO)에 비해 TypeScript의 가장 큰 장점이기 때문에 typechecking을 원했습니다. 또한 다른 개발자가 새로운 엔드 포인트를 추가 할 수있는 환경을 만들고 가능한 한 원활하게 API를 만들고 싶었습니다.
환경 (dev, test, stage, prod, ""등)을받는 클래스를 만들고이 값을 각 API 컬렉션에 대한 기본 URL을 만드는 일련의 클래스 [1-N]에 전달했습니다. . 각 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/");
}
}