Roy*_*mir 66 javascript angular
我想在App Settings
我的应用程序中添加一个部分,其中包含一些consts和预定义的值.
我已经阅读了这个使用的答案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');
Run Code Online (Sandbox Code Playgroud)
这是我想要使用这些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 {
}
Run Code Online (Sandbox Code Playgroud)
但它不起作用,我得到错误.
题:
如何以Angular方式使用"app.settings"值?
NB当然我可以创建Injectable服务并将其放在NgModule的提供者中,但正如我所说,我想用InjectionToken
Angular方式来做.
til*_*ilo 128
如果您使用的是angular-cli,还有另一种选择:
Angular CLI提供环境文件src/environments
(默认为environment.ts
(dev)和environment.prod.ts
(生产)).
请注意,您需要在所有environment.*
文件中提供配置参数,例如,
environment.ts:
export const environment = {
production: false,
apiEndpoint: 'http://localhost:8000/api/v1'
};
Run Code Online (Sandbox Code Playgroud)
environment.prod.ts:
export const environment = {
production: true,
apiEndpoint: '__your_production_server__'
};
Run Code Online (Sandbox Code Playgroud)
并在您的服务中使用它们(自动选择正确的环境文件):
api.service.ts
// ... other imports
import { environment } from '../../environments/environment';
@Injectable()
export class ApiService {
public apiRequest(): Observable<MyObject[]> {
const path = environment.apiEndpoint + `/objects`;
// ...
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
在Github(Angular CLI版本6)或官方Angular指南(版本7)中阅读有关应用程序环境的更多信息.
mtp*_*ltz 46
我想出了如何使用InjectionTokens执行此操作(请参阅下面的示例),如果您的项目是使用Angular CLI
您构建的,则可以使用/environments
静态文件中的环境文件(application wide settings
如API端点),但根据项目的要求,您很可能最终会使用两者既然环境文件只是对象文字,而使用InjectionToken
's的可注入配置可以使用环境变量,因为它是一个类可以应用逻辑来根据应用程序中的其他因素配置它,例如初始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 { }
Run Code Online (Sandbox Code Playgroud)
/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 { }
Run Code Online (Sandbox Code Playgroud)
现在你可以把它转换成任何组件,服务等:
/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);
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
然后,您还可以使用导出的AppConfig键入检查配置.
Mat*_*ter 39
建议不要将environment.*.ts
文件用于API url配置。似乎您应该这样做,因为它提到了“环境”一词。
使用此实际上是编译时配置。如果要更改API网址,则需要重新构建。那是你不想做的事情...只问你友好的质量保证部门:)
您需要的是运行时配置,即应用程序在启动时加载其配置。
其他一些答案也涉及到这一点,但不同之处在于,应用程序启动后需要立即加载配置,以便普通服务可以在需要时使用它。
要实现运行时配置:
/src/assets/
文件夹(以便在构建时复制)AppConfigService
以加载和分发配置APP_INITIALISER
/src/assets
您可以将其添加到另一个文件夹,但是您需要告诉CLI它是中的资产angular.json
。使用资产文件夹开始:
{
"apiBaseUrl": "https://development.local/apiUrl"
}
Run Code Online (Sandbox Code Playgroud)
AppConfigService
这是将在需要配置值时注入的服务:
{
"apiBaseUrl": "https://development.local/apiUrl"
}
Run Code Online (Sandbox Code Playgroud)
APP_INITIALISER
为了在AppConfigService
配置完全加载的情况下安全地注入,我们需要在应用启动时加载配置。重要的是,初始化工厂函数需要返回a,Promise
以便Angular知道要等到其完成解析后才能完成启动:
@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;
}
}
Run Code Online (Sandbox Code Playgroud)
现在,您可以将其注入到任何需要的地方,所有配置都可以读取:
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 { }
Run Code Online (Sandbox Code Playgroud)
我说的不够强烈,将您的API网址配置为编译时配置是一种反模式。使用运行时配置。
Gle*_*enn 13
我发现APP_INITIALIZER
在其他服务提供商要求注入配置的情况下使用 an 不起作用。它们可以在APP_INITIALIZER
运行之前实例化。
我见过其他解决方案,用于读取 config.json 文件并在引导根模块之前fetch
使用参数中的注入令牌提供它。platformBrowserDynamic()
但fetch
并非所有浏览器都支持,特别是我的目标移动设备的 WebView 浏览器。
The following is a solution that works for me for both PWA and mobile devices (WebView). Note: I've only tested in Android so far; working from home means I don't have access to a Mac to build.
In 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();
Run Code Online (Sandbox Code Playgroud)
This code:
config.json
file. APP_CONFIG
injection token, prior to bootstrapping. APP_CONFIG
can then be injected into any additional providers in app-module.ts
and it will be defined. For example, I can initialise the FIREBASE_OPTIONS
injection token from @angular/fire
with the following:
{
provide: FIREBASE_OPTIONS,
useFactory: (config: IConfig) => config.firebaseConfig,
deps: [APP_CONFIG]
}
Run Code Online (Sandbox Code Playgroud)
I find this whole thing a surprisingly difficult (and hacky) thing to do for a very common requirement. Hopefully in the near future there will be a better way, such as, support for async provider factories.
The rest of the code for completeness...
In 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');
Run Code Online (Sandbox Code Playgroud)
and in app/lib/config/config.ts
I define the interface for my JSON config file:
export interface IConfig {
name: string;
version: string;
instance: string;
firebaseConfig: {
apiKey: string;
// etc
}
}
Run Code Online (Sandbox Code Playgroud)
Config is stored in assets/config/config.json
:
{
"name": "my-app",
"version": "#{Build.BuildNumber}#",
"instance": "localdev",
"firebaseConfig": {
"apiKey": "abcd"
...
}
}
Run Code Online (Sandbox Code Playgroud)
Note: I use an Azure DevOps task to insert Build.BuildNumber and substitute other settings for different deployment environments as it is being deployed.
小智 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);
}
}
Run Code Online (Sandbox Code Playgroud)
和config.json
{
"apiUrl": "http://localhost:3000/api"
}
Run Code Online (Sandbox Code Playgroud)
穷人的配置文件:
添加到 index.html 作为 body 标签的第一行:
<script lang="javascript" src="assets/config.js"></script>
Run Code Online (Sandbox Code Playgroud)
添加资产/config.js:
var config = {
apiBaseUrl: "http://localhost:8080"
}
Run Code Online (Sandbox Code Playgroud)
添加 config.ts:
export const config: AppConfig = window['config']
export interface AppConfig {
apiBaseUrl: string
}
Run Code Online (Sandbox Code Playgroud)
有相当多的文章建议您使用AppConfigService
诸如此之类的方法来获取 Angular 配置设置。
但我发现有时这不起作用。
拥有一个“ config.json ”文件更简单、更可靠,然后创建一个只读入它并返回一个值的类,例如我的配置文件如下所示:
{
"appName": "Mike's app",
"version": "1.23.4",
"logging_URL" : "https://someWebsite.azurewebsites.net/logs"
}
Run Code Online (Sandbox Code Playgroud)
我将使用以下方法访问这些值:
import config from '../../assets/config.json';
@Injectable({
providedIn: 'root'
})
export class AppConfigService {
get appName() {
return config.appName;
}
get appVersion() {
return config.version;
}
get loggingUrl() {
return config.logging_URL;
}
}
Run Code Online (Sandbox Code Playgroud)
(几个月后……)
在庆幸自己做出了一个更简单的 Angular 解决方案后,我意识到这有一个很大的缺点。如果您使用 AppConfigService,并且使用 CI/CD,那么您可以让构建过程更新 config .json 文件,并且 Angular 应用程序将使用这些设置。
对于我的版本,是的,它更简单,但是没有可以覆盖的配置设置文件。对于自动化构建过程,这可能是不可取的。
归档时间: |
|
查看次数: |
70220 次 |
最近记录: |