我有一个带有服务器网址的 app-const.ts :
export class AppConst {
public static serverPath = 'http://10.0.0.126:3031';
}
这是Spring Boot REST服务器的URL路径。 在这种情况下,我将此常量保持在一个位置并在所有模块中使用它。 然而 在构建之后我无法更改此常量,而无需在服务器URL更改时重新构建整个项目。
有没有办法在托管(index.html旁边)的某些外部配置文件中保持此常量,以便我可以在不重建项目的情况下更改它(如应用程序。 Spring Boot中的属性文件,谁知道??
或者我如何通过更改服务器URL轻松管理情况?
添加即可。清除情况:我将Angular Web客户端放在托管上。然后,此客户端开始与可以放置在某处(例如,在云中)的Spring Boot REST服务器进行通信。此Spring Boot服务器有一个服务器URL(serverPath),有时可能会更改。 现在,如果服务器URL更改,我需要更改此serverPath常量并仅因此常量而重建整个Angular项目。
答案 0 :(得分:2)
我有以下解决方案。它使用外部JSON配置文件。
首先在 assets / data文件夹中创建一个JSON。
config.json :
{ “serverPath”:“http://10.0.0.126:3031” }
然后阅读并解析它。
config.service.ts :
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class ConfigService {
private configUrl = "assets/data/config.json";
constructor(private http: HttpClient) {
}
public getJSON(): Observable<any> {
return this.http.get(this.configUrl)
}
public getSavedServerPath(){
return localStorage.getItem('serverPath');
}
}
在app.module.ts中,您需要导入HttpClientModule以便这样做 的工作原理。
然后,您可以在登录组件的LocalStorage中保存serverPath,例如。
login.component.ts :
constructor(public loginService:LoginService, public configService:ConfigService, private router: Router) {
}
ngOnInit() {
this.configService.getJSON().subscribe(data => {
localStorage.setItem("serverPath", data["serverPath"]);
});
...
}
之后,您可以在所有其他服务中访问serverPath。
server.service.ts :
import {Injectable } from '@angular/core';
import {Headers, Http, Response} from '@angular/http';
import 'rxjs/Rx';
import {Observable} from 'rxjs/Observable';
import {ConfigService} from '../services/config.service';
@Injectable()
export class ServerService {
private serverPath:string;
constructor(public configService: ConfigService, private http:Http) {
this.serverPath = this.configService.getSavedServerPath();
}
...
}
构建完成后,您将在 dist 文件夹中看到 assets / data / config.json 文件。 将所有 dist 文件夹复制到您的托管服务中并进行所有工作。
答案 1 :(得分:1)
另一种解决方案是将其作为javascript变量添加到index.html文件中。我使用了这种方法,并且有效。
使用“ script”标签将其添加到index.html的“ head”部分,例如:
this
(我的全局变量名为“ LMS_REST_API_URL”)
之后,您可以像这样访问此变量:
function myFunc(){
a = 10;
console.log(a); // 10
if(true){
let a = 20;
console.log(a); // 20
console.log(this.a); // 10 ... voit lá. Mind context of execution!
};
};
myFunc();
我直接从需要URL的服务中使用它,但是它可能也可以像您正在使用的那样在单独的app-const.ts类文件中工作。
答案 2 :(得分:0)
我有几个应用程序正是这样做的。 我已经为我的应用程序构建了一个实用程序库,包括这个。
首先,我有一个“配置”类。 json配置文件从服务器加载并映射到此类的实例:
export class Configuration {
[key: string]: any;
}
然后,有ConfigurationService,它负责加载配置文件:
import {APP_INITIALIZER, Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs/Observable';
import {AsyncSubject} from 'rxjs/AsyncSubject';
import 'rxjs/observable/throw';
import {Configuration} from './configuration';
// synchronous version of the initializer - the app initialization will wait for the configuration to load
export function configurationServiceInitializerFactory(configurationService: ConfigurationService): Function {
// a lambda is required here, otherwise `this` won't work inside ConfigurationService::load
return () => configurationService.load(Synchronicity.Sync);
}
// async version of the initializer - the app initialization will proceed without waiting for the configuration to load
export function asyncConfigurationServiceInitializerFactory(configurationService: ConfigurationService): Function {
// a lambda is required here, otherwise `this` won't work inside ConfigurationService::load
return () => {
configurationService.load(Synchronicity.Async);
return null;
};
}
export const enum Synchronicity {
Sync,
Async,
Unknown
}
@Injectable()
export class ConfigurationService {
private synchronicity: Synchronicity = Synchronicity.Unknown;
// the observable from the (load) http call to get the configuration
private httpObservable: Observable<Configuration>;
// the error (if any) that occurred during the load
private loadError;
private loadAttempted = false;
private hasError = false;
private loaded = false;
// Observable that makes the config available to consumers when using async initialization
private loadSubject = new AsyncSubject<Configuration>();
// the configuration
private configuration: Configuration;
constructor(private http: HttpClient) {
}
public hasLoadError(): boolean {
return this.hasError;
}
public isLoadead(): boolean {
return this.loaded;
}
// use this when you have initialized with the (synchronous) configurationServiceInitializerFactory
public getConfig(): Configuration {
if(!this.loadAttempted) {
throw new Error('ConfigurationService.getConfig() - service has not been iniialized yet');
}
if(this.synchronicity === Synchronicity.Async) {
throw new Error('ConfigurationService.getConfig() - service has been iniialized async - use getConfigurationObserable()');
}
if(this.hasError) {
throw this.loadError;
}
if(!this.loaded) {
throw new Error('ConfigurationService.getConfig() - service has not finished loading the config');
}
return this.configuration;
}
// use this when you have initialized with the asyncCnfigurationServiceInitializerFactory
public getConfigObservable(): Observable<Configuration> {
// if neither init function was used, init async
if (!this.loadAttempted) {
this.load(Synchronicity.Async);
}
return this.loadSubject;
}
// the return value (Promise) of this method is provided via the APP_INITIALIZER Injection Token,
// so the application's initialization will not complete until the Promise resolves.
public load(synchronicity: Synchronicity): Promise<Configuration> {
if (!this.loadAttempted) {
this.loadAttempted = true;
this.synchronicity = synchronicity;
this.httpObservable = this.http.get<Configuration>('config/ui-config.json'); // path is relative to that for app's index.html
this.httpObservable.subscribe(
config => {
this.configuration = config;
this.loadError = undefined;
this.hasError = false;
this.loadSubject.next(this.configuration);
this.loadSubject.complete();
this.loaded = true;
},
error => {
this.loadError = error;
this.hasError = true;
this.loadSubject.error(error);
this.loadSubject.complete();
}
);
return this.httpObservable.toPromise();
}
}
}
如您所见,此服务从相对路径config / ui-config.json获取配置。该路径相对于为引导应用程序而加载的index.html文件。您需要安排服务器从该位置返回配置文件。
该服务将挂钩到Angular的初始化序列(后面的代码)。它可以与应用程序的初始化同步或异步完成。
如果使用'synchronous'方法,则在加载json文件时,应用程序初始化将暂停。这样做的好处是,一旦应用程序完成初始化,就知道配置可用。缺点是初始化期间潜在的长暂停,用户正在查看空白页。
如果您使用'异步'方法,则应用初始化将仅启动对配置文件的请求,但不会暂停以等待该请求完成。上行:快速(正常)初始化。缺点:你得到一个Observable of the Configuration而不是一个Configuration,所以你需要在那个Observable上的flatMap(mergeMap)到你需要配置的地方。
以下是app.module中应用初始化的方式:
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
// etc.
],
providers: [
ConfigurationService,
{ provide: APP_INITIALIZER, useFactory: asyncConfigurationServiceInitializerFactory, deps: [ConfigurationService], multi: true },
],
bootstrap: [AppComponent]
})
export class AppModule {
}
这是异步配置的一个例子。要进行同步,只需使用configurationServiceInitializerFactory
代替asyncConfigurationServiceInitializerFactory
同样,如果你使用同步版本,你可以将ConfigurationService注入你的服务,并调用它的getConfig()
方法。
如果您使用异步版本,您仍然会将ConfigurationService注入您的服务,但是您需要执行以下操作:
getSomething(): Observable<Something> {
return this.configurationService.getConfigObservable().mergeMap(config =>
this.http.get<Something>(`${config.serviceRoot}/something`)
);
}
编辑:哦,我差点忘了,我前一段时间做了一篇博文,而且还有一些细节。它位于https://chariotsolutions.com/blog/post/12-factor-ish-configuration-of-angular-applications/
的GitHub上有一个完整的例子答案 3 :(得分:0)
我使用资产文件夹引用了外部配置。想法是,部署过程会将针对该环境的配置更新到资产文件夹中,然后在应用启动时读取该文件夹。为此,首先将您的配置放在src/app/assets/config.json
中。例如
{
"serverRoot":"https://my.server.root.com/"
}
然后,部署过程可以将该文件中的serverRoot
属性更新为该环境的正确值,或完全替换config.json
的内容。
然后,在src/app/model/environment.ts
中创建一个模型类,这将允许类型安全地访问配置:
export class Environment {
static serverRoot:string;
static load(config:json) {
this.serverRoot = config.serverRoot;
}
}
然后,创建服务以将配置加载到src/app/services/environment-load.service.ts
:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Environment } from '../model/Environment';
@Injectable({
providedIn: 'root'
})
export class EnvironmentLoadService {
constructor(private http: HttpClient) { }
init() {
return this.http.get('assets/config.json').toPromise().then(data => {
Environment.load(data);
});
}
static initializeEnvironmentConfig = (appConfig: EnvironmentLoadService) => {
return () => {
return appConfig.init();
};
};
}
最后,在您的应用模块(src/app/app.module.ts
)中,将EnvironmentLoadService
设置为在应用程序生命周期的应用程序初始化阶段创建的提供者。这样可以保证在应用程序初始化阶段完成之前所有承诺都得到解决,并且在构造第一个组件之前就可以完全加载配置:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';
import { EnvironmentLoadService } from './services/environment-load.service';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [EnvironmentLoadService, {
provide: APP_INITIALIZER,
useFactory: EnvironmentLoadService.initializeEnvironmentConfig,
multi: true,
deps: [EnvironmentLoadService]
}],
bootstrap: [AppComponent]
})
export class AppModule { }