使用一些配置文件在Angular 5中完全外部常量

问题描述 投票:4回答:3

我有一个带有服务器URL的app-const.ts:

export class AppConst {
  public static serverPath = 'http://10.0.0.126:3031';
}

这是Spring Boot REST服务器的URL路径。在这种情况下,我将此常量保持在一个位置并在所有模块中使用它。但是,如果更改了服务器URL,那么在构建之后,如果不重新构建整个项目,我就无法更改此常量。

有没有办法在托管(index.html旁边)的某个外部配置文件中保持这个常量,这样我就可以在不重建项目的情况下更改它(比如Spring Boot中的application.properties文件,谁知道)?

或者我如何通过更改服务器URL轻松管理情况?

加成。清除情况:我将Angular Web客户端放在托管上。然后,此客户端开始与可以放置在某处(例如,在云中)的Spring Boot REST服务器进行通信。此Spring Boot服务器有一个服务器URL(serverPath),有时可能会更改。现在,如果服务器URL更改,我需要更改此serverPath常量并仅由于此常量重建整个Angular项目。

angular file constants
3个回答
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以便这样做。

然后,您可以将登录组件中的serverPath保存在LocalStorage中。

login.component.ts:

  constructor(public loginService:LoginService, public configService:ConfigService, private router: Router) {
  }

  ngOnInit() {

    this.configService.getJSON().subscribe(data => {
      localStorage.setItem("serverPath", data["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
投票

另一种解决方案是将其添加为index.html文件中的javascript变量。我使用这种方法,它的工作原理。

使用“script”标记将其添加到index.html的“head”部分,例如:

<head>
  <script>
    window.LMS_REST_API_URL = "http://192.168.0.111:3000/";
  </script>
...

(我的全局变量名为“LMS_REST_API_URL”)

在此之后,您可以像这样访问此变量:

private lms_cli_URL = window["LMS_REST_API_URL"];

我直接从需要URL的服务中使用它,但它可能也适用于你正在使用的单独的app-const.ts类文件。


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的初始化序列(后面的代码)。它可以与应用程序的初始化同步或异步完成。

如果使用“同步”方法,则在加载json文件时,应用程序初始化将暂停。这样做的好处是,一旦应用程序完成初始化,就知道配置可用。缺点是初始化期间潜在的长暂停,用户正在查看空白页面。

如果您使用'异步'方法,则应用程序初始化将仅启动对配置文件的请求,但不会暂停以等待该请求完成。上行:快速(正常)初始化。缺点:您获得了配置的Observable而不是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/

https://github.com/rfreedman/angular-configuration-service的GitHub上有一个完整的例子

© www.soinside.com 2019 - 2024. All rights reserved.