App Settings
일부 const 및 사전 정의 된 값을 포함 할 섹션을 내 앱 에 추가하고 싶습니다 .
나는 이미 사용하는 이 답변 을 읽었 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');
그리고 이것은 내가 그 const를 사용하려는 구성 요소입니다.
//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 {
}
하지만 작동하지 않고 오류가 발생합니다.
질문:
Angular 방식으로 “app.settings”값을 어떻게 사용할 수 있습니까?
NB 물론 Injectable 서비스를 생성하여 NgModule 공급자에 넣을 수 있습니다. 그러나 제가 말했듯 InjectionToken
이 Angular 방식으로 하고 싶습니다 .
답변
InjectionTokens (아래 예제 참조)를 사용하여이 작업을 수행하는 방법을 알아 냈고 프로젝트가를 사용하여 빌드 된 경우 API 엔드 포인트와 같은 정적 Angular CLI
환경 파일을 사용할 수 있지만 프로젝트의 요구 사항에 따라 대부분 환경 파일은 객체 리터럴 일 뿐이며 ‘s를 사용 하는 주입 가능한 구성 은 환경 변수를 사용할 수 있으며 클래스이기 때문에 초기 http 요청 데이터, 하위 도메인과 같은 응용 프로그램의 다른 요소를 기반으로 구성하는 논리를 적용 할 수 있습니다. 등/environments
application wide settings
InjectionToken
주입 토큰 예
/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를 사용하여 구성 확인을 입력 할 수도 있습니다.
답변
사용하는 경우 각도 CLI, 또 다른 옵션이 있습니다.
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)의 애플리케이션 환경에 대해 자세히 알아보세요 .
답변
environment.*.ts
API URL 구성에 파일 을 사용하는 것은 바람직하지 않습니다 . 이것은 “환경”이라는 단어를 언급하기 때문에 그렇게해야 할 것 같습니다.
이것을 사용하는 것은 실제로 컴파일 타임 구성 입니다. API URL을 변경하려면 다시 빌드해야합니다. 하고 싶지 않은 일입니다. 친절한 QA 부서에 문의하세요. 🙂
필요한 것은 런타임 구성입니다 . 즉, 앱이 시작될 때 구성을로드합니다.
다른 답변은 이것에 대해 다루고 있지만 차이점은 앱이 시작되는 즉시 구성을로드해야 하므로 필요할 때마다 일반 서비스에서 사용할 수 있다는 것입니다.
런타임 구성을 구현하려면 :
/src/assets/
폴더에 JSON 구성 파일 추가 (빌드시 복사 됨)AppConfigService
구성을로드하고 배포하기 위한 만들기- 다음을 사용하여 구성로드
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
구성이 완전히로드 된 상태에서를 안전하게 삽입 하려면 앱 시작시 구성을로드해야합니다. 중요한 것은 초기화 팩토리 함수가 a를 반환 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;
}
}
충분히 강력하게 말할 수는 없지만 컴파일 타임 구성이 안티 패턴이므로 API URL을 구성합니다 . 런타임 구성을 사용하십시오.
답변
여기 내 솔루션이 있습니다. .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"
}
답변
가난한 사람의 구성 파일 :
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
}
답변
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();
이 코드 :
config.json
파일에 대한 비동기 요청을 시작 합니다.- 요청이 완료되면 JSON을 Javascript 객체로 구문 분석합니다.
APP_CONFIG
부트 스트랩 전에 주입 토큰을 사용하여 값을 제공합니다 .- 마지막으로 루트 모듈을 부트 스트랩합니다.
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를 삽입하고 배포중인 다른 배포 환경에 대한 다른 설정을 대체합니다.
답변
여기에 대한 두 가지 해결책이 있습니다.
1. json 파일에 저장
json 파일을 만들고 $http.get()
메서드 별로 구성 요소를 가져옵니다 . 이것이 매우 낮게 필요하다면 좋고 빠릅니다.
2. 데이터 서비스를 사용하여 저장
모든 구성 요소에 저장 및 사용하거나 사용량이 많은 경우 데이터 서비스를 사용하는 것이 좋습니다. 이렇게 :
-
폴더 안에 정적 폴더를 만드십시오
src/app
. -
로 이름이 지정된 파일
fuels.ts
을 정적 폴더에 만듭니다 . 여기에 다른 정적 파일도 저장할 수 있습니다. 이와 같이 데이터를 정의하십시오. 연료 데이터가 있다고 가정합니다.
__
export const Fuels {
Fuel: [
{ "id": 1, "type": "A" },
{ "id": 2, "type": "B" },
{ "id": 3, "type": "C" },
{ "id": 4, "type": "D" },
];
}
- 파일 이름 static.services.ts 만들기
__
import { Injectable } from "@angular/core";
import { Fuels } from "./static/fuels";
@Injectable()
export class StaticService {
constructor() { }
getFuelData(): Fuels[] {
return Fuels;
}
}`
- 이제 모든 모듈에서 이것을 사용할 수 있습니다.
이와 같이 app.module.ts 파일을 가져오고 공급자를 변경하십시오.
import { StaticService } from './static.services';
providers: [StaticService]
이제 이것을 다음과 같이 사용하십시오. StaticService
다른 모듈 .
그게 다야.