使用一些配置文件

时间:2018-04-18 15:02:50

标签: angular file constants

我有一个带有服务器网址的 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项目。

4 个答案:

答案 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/

https://github.com/rfreedman/angular-configuration-service

的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 { }