App.settings - Angular 方式?

Posted

技术标签:

【中文标题】App.settings - Angular 方式?【英文标题】:App.settings - the Angular way? 【发布时间】:2017-08-28 19:35:58 【问题描述】:

我想在我的应用程序中添加一个App Settings 部分,其中将包含一些常量和预定义值。

我已经阅读了使用OpaqueTokenthis answer,但它在Angular 中已被弃用。这个article 解释了差异,但没有提供完整的示例,我的尝试没有成功。

这是我尝试过的(我不知道这是否正确):

//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”值?

plunker

NB 当然,我可以创建 Injectable 服务并将其放入 NgModule 的提供者中,但正如我所说,我想用 InjectionToken 来做到这一点,Angular 方式。

【问题讨论】:

您可以根据当前官方文档查看我的答案here @javier 没有。如果两个提供商提供相同的名称,您的链接就有问题,所以您现在遇到了问题。输入 opaquetoken 你知道[不推荐使用OpaqueToken]。 (angular.io/api/core/OpaqueToken)This Article 谈论如何防止 Angular Providers 中的名称冲突 是的,我知道,但链接的文章仍然是错误的。 下面的链接可能对每个喜欢使用新的角度配置架构架构的人都有帮助devblogs.microsoft.com/premier-developer/… 【参考方案1】:

如果您使用angular-cli,还有另一种选择:

Angular CLI 在src/environments 中提供环境文件(默认为environment.ts(开发)和environment.prod.ts(生产))。

请注意,您需要在所有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 version 6) 或official Angular guide (version 7) 上阅读有关应用程序环境的更多信息。

【讨论】:

它工作正常。但是在移动构建时它也被更改为捆绑包。我应该在我的服务中更改配置而不是在移动到生产后的代码中 这在正常的软件开发中有点反模式; API url 只是配置。无需重新构建即可针对不同的环境重新配置应用程序。它应该构建一次,部署多次(pre-prod、staging、prod 等)。 @MattTester 这实际上是现在官方的 Angular-CLI 故事。如果您碰巧对这个问题有更好的答案:请随时发布! ng build 后是否可以配置? 哦,好吧,我看错了 cmets。我同意这有助于反模式,我认为动态运行时配置有一个故事。【参考方案2】:

不建议将environment.*.ts 文件用于您的 API URL 配置。看起来你应该这样做,因为这里提到了“环境”这个词。

使用这个其实是编译时配置。如果要更改 API URL,则需要重新构建。那是您不想做的事情......只需询问您友好的质量检查部门:)

您需要的是运行时配置,即应用在启动时加载其配置。

其他一些答案涉及到这一点,但不同之处在于配置需要在应用启动后立即加载,以便普通服务在需要时可以使用它。

实现运行时配置:

    将 JSON 配置文件添加到 /src/assets/ 文件夹(以便在构建时复制) 创建AppConfigService 以加载和分发配置 使用APP_INITIALIZER 加载配置

1。将配置文件添加到/src/assets

您可以将它添加到另一个文件夹,但您需要通过更新 angular.json 配置文件来告诉 Angular CLI 它是一项资产。开始使用 assets 文件夹:


  "apiBaseUrl": "https://development.local/apiUrl"

2。创建AppConfigService

这是在您需要配置值时将被注入的服务:

import  HttpClient  from '@angular/common/http';
import  Injectable  from '@angular/core';

@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 安全注入,配置完全加载,我们需要在应用启动时加载配置。重要的是,初始化工厂函数需要返回一个Promise,以便 Angular 知道在完成启动之前等待它完成解析:

import  APP_INITIALIZER  from '@angular/core';
import  AppConfigService  from './services/app-config.service';

@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 配置为编译时配置是一种反模式。使用运行时配置。

【讨论】:

本地文件或其他服务,编译时配置不应用于 API url。想象一下,如果您的应用程序作为产品出售(购买者安装),您不希望他们编译它等等。无论哪种方式,您都不想重新编译 2 年前构建的东西,只是因为API 网址已更改。风险!! @Bloodhound 你可以拥有多个APP_INITIALIZER,但我认为你不能轻易地让它们相互依赖。听起来您有一个很好的问题要问,所以也许可以在这里链接到它? @MattTester - 如果 Angular 实现了这个功能,它将解决我们的问题:github.com/angular/angular/issues/23279#issuecomment-528417026 @CrhistianRamirez 从应用程序的角度来看:直到运行时才知道配置,静态文件在构建之外,可以在部署时以多种方式设置。静态文件适用于非敏感配置。 API 或其他受保护的端点可以使用相同的技术,但如何进行身份验证以使其受到保护是您的下一个挑战。 @DaleK 从字里行间看,您正在使用 Web Deploy 进行部署。如果您使用的是部署管道,例如 Azure DevOps,则可以在下一步中正确设置配置文件。配置的设置是部署过程/管道的责任,它可以覆盖默认配置文件中的值。希望澄清。【参考方案3】:

我想出了如何使用 InjectionTokens 执行此操作(请参见下面的示例),如果您的项目是使用 Angular CLI 构建的,您可以使用在 /environments 中找到的环境文件作为静态 application wide settings 像 API 端点一样,但是根据您项目的要求,您很可能最终会同时使用这两种文件,因为环境文件只是对象文字,而使用 InjectionToken 的可注入配置可以使用环境变量,并且因为它是一个类,所以可以应用逻辑来配置它关于应用程序中的其他因素,例如初始 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  

/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 键入检查配置。

【讨论】:

不,但您可以直接将第一部分复制并粘贴到文件中,将其导入您的 app.module.ts 文件中,然后在任何地方进行 DI 并将其输出到控制台。我需要更长的时间才能在 plunker 中进行设置,然后执行这些步骤。 哦,我以为您已经为此准备好了 :-) 谢谢。 对于那些想要的人:plnkr.co/edit/2YMZk5mhP1B4jTgA37B8?p=preview 我认为您不需要导出 AppConfig 接口/类。做 DI 时绝对不需要使用它。为了在一个文件中进行这项工作,它必须是一个类而不是一个接口,但这并不重要。事实上,样式指南建议使用类而不是接口,因为这意味着更少的代码,您仍然可以使用它们进行类型检查。关于 InjectionToken 通过泛型使用它,您需要包括在内。 我正在尝试使用 Azure 的环境变量和 JSON 转换功能动态注入 API 端点,但看起来这个答案只是从环境文件中获取 apiEndpoint。您将如何从配置中获取并导出它?【参考方案4】:

我发现在其他服务提供者需要注入配置的情况下,使用APP_INITIALIZER 是行不通的。它们可以在APP_INITIALIZER 运行之前被实例化。

我已经看到其他解决方案使用fetch 读取 config.json 文件并在引导根模块之前使用platformBrowserDynamic() 的参数中的注入令牌提供它。但并非所有浏览器都支持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 中的任何其他提供程序中,并且它将被定义。例如,我可以使用以下代码从@angular/fire 初始化FIREBASE_OPTIONS 注入令牌:


      provide: FIREBASE_OPTIONS,
      useFactory: (config: IConfig) => config.firebaseConfig,
      deps: [APP_CONFIG]

对于一个非常常见的需求,我发现这整个事情是一件非常困难(和 hacky)的事情。希望在不久的将来会有更好的方法,比如支持异步提供者工厂。

为了完整性,剩下的代码...

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 并在部署时将其他设置替换为不同的部署环境。

【讨论】:

谢谢@Glenn,这正是我所需要的。 谢谢,虽然有问题。 configListener() 应该是 configListener(response: any) 并且你应该解析 const configuration = JSON.parse(response.target.responseText) 因为没有 this.responseText。明确地说,您实际上可以在任何类(组件、服务)的构造函数中使用 @Inject(APP_CONFIG) private config: IConfig 来访问配置值。【参考方案5】:

这是我的解决方案,从 .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"

【讨论】:

这种方法的问题在于 config.json 是对外开放的。你会如何阻止别人输入 www.mywebsite.com/assets/config.json? @AlbertoL.Bonfiglio 您将服务器配置为不允许从外部访问 config.json 文件(或将其放在没有公共访问权限的目录中) 这也是我最喜欢的解决方案,但还是要担心安全风险。 拜托,你能帮我把它弄好吗?对于角度环境,它比传统的风险更大吗?在ng build --prod 之后的environments.prod.ts 的全部内容将在某个时间出现在某个.js 文件中。即使经过混淆处理,来自environments.prod.ts 的数据也会以明文形式显示。和所有 .js 文件一样,它将在最终用户计算机上可用。 @AlbertoL.Bonfiglio 因为 Angular 应用程序本质上是客户端应用程序,并且 JavaScript 将用于传递数据和配置,所以其中不应该使用秘密配置;所有秘密配置定义都应位于用户浏览器或浏览器工具无法访问的 API 层后面。公众可以访问 API 的基本 URI 等值,因为 API 应该有自己的凭据和基于用户登录的安全性(通过 https 的不记名令牌)。【参考方案6】:

穷人的配置文件:

添加到您的 index.html 作为正文标记中的第一行:

<script lang="javascript" src="assets/config.js"></script>

添加资产/config.js:

var config = 
    apiBaseUrl: "http://localhost:8080"

添加 config.ts:

export const config: AppConfig = window['config']

export interface AppConfig 
    apiBaseUrl: string

【讨论】:

说真的,+1 用于将解决方案简化为最基本的组件并仍然保持类型一致性。【参考方案7】:

这是我的两个解决方案

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

就是这样。

【讨论】:

很好的解决方案,因为您不必重新编译。环境就像在代码中硬编码它。可恶的。 +1【参考方案8】:

我发现来自 Microsoft Dev 博客的 Angular How-to: Editable Config Files 是最佳解决方案。您可以配置 dev 构建设置或 prod 构建设置。

【讨论】:

【参考方案9】:

不少文章建议您使用 AppConfigService such as this one 获取 Angular 配置设置。

但我发现有时这不起作用。

拥有一个“config.json”文件更简单,更可靠,然后创建一个只读取它的类,并返回一个值,例如我的配置文件看起来像这样:


  "appName": "Mike's app",
  "version": "1.23.4",
  "logging_URL" : "https://someWebsite.azurewebsites.net/logs"

我会使用这个来访问这些值:

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; 
    

【讨论】:

【参考方案10】:

几年前,在我加入并制定使用本地存储来存储用户和环境信息的解决方案之前,我们就遇到了这个问题。确切地说,Angular 1.0 天。我们以前在运行时动态创建一个 js 文件,然后将生成的 api url 放入一个全局变量中。这些天来,我们更多地受到 OOP 驱动,并且不使用本地存储做任何事情。

我为确定环境和创建 api url 创建了一个更好的解决方案。

这有什么不同?

除非加载了 config.json 文件,否则应用程序不会加载。它使用工厂函数来创建更高程度的 SOC。我可以将它封装到一个服务中,但是当文件的不同部分之间的唯一相似之处是它们一起存在于文件中时,我从来没有看到任何理由。如果它能够接受一个函数,那么拥有一个工厂函数可以让我将函数直接传递给一个模块。最后,当工厂功能可用时,我可以更轻松地设置 InjectionTokens。

缺点?

如果您要配置的模块不允许将工厂函数传递给 forRoot() 或 forChild(),并且没有使用工厂函数配置包的其他方式。

说明

    使用 fetch 检索 json 文件,我将对象存储在窗口中并引发自定义事件。 - 记得安装 whatwg-fetch 并将其添加到您的 polyfills.ts 以实现 IE 兼容性 让事件监听器监听自定义事件。 事件侦听器接收事件,从窗口中检索对象以传递给可观察对象,并清除窗口中存储的内容。 引导角度

-- 这就是我的解决方案开始真正不同的地方--

    创建一个导出接口的文件,该接口的结构代表您的 config.json——它确实有助于类型一致性,下一段代码需要一个类型,当你知道你的时候不要指定 any可以指定更具体的内容 创建 BehaviorSubject,您将在第 3 步中将解析后的 json 文件传递​​给它。 使用工厂函数引用配置的不同部分以维护 SOC 为需要工厂函数结果的提供者创建 InjectionToken

-- 和/或--

    将工厂函数直接传递到能够在其 forRoot() 或 forChild() 方法中接受函数的模块中。

-- main.ts

我在创建事件侦听器之前检查 window["environment"] 是否未填充,以允许在 main.ts 中的代码执行之前通过其他方式填充 window["environment"] 的解决方案的可能性。

import  enableProdMode  from '@angular/core';
import  platformBrowserDynamic  from '@angular/platform-browser-dynamic';
import  AppModule  from './app/app.module';
import  configurationSubject  from './app/utils/environment-resolver';

var configurationLoadedEvent = document.createEvent('Event');
configurationLoadedEvent.initEvent('config-set', true, true);
fetch("../../assets/config.json")
.then(result =>  return result.json(); )
.then(data => 
  window["environment"] = data;
  document.dispatchEvent(configurationLoadedEvent);
, error => window.location.reload());

/*
  angular-cli only loads the first thing it finds it needs a dependency under /app in main.ts when under local scope. 
  Make AppModule the first dependency it needs and the rest are done for ya. Event listeners are 
  ran at a higher level of scope bypassing the behavior of not loading AppModule when the 
  configurationSubject is referenced before calling platformBrowserDynamic().bootstrapModule(AppModule)

  example: this will not work because configurationSubject is the first dependency the compiler realizes that lives under 
  app and will ONLY load that dependency, making AppModule an empty object.

  if(window["environment"])
  
    if (window["environment"].production) 
      enableProdMode();
    
    configurationSubject.next(window["environment"]);
    platformBrowserDynamic().bootstrapModule(AppModule)
    .catch(err => console.log(err));
  
*/
if(!window["environment"]) 
  document.addEventListener('config-set', function(e)
    if (window["environment"].production) 
      enableProdMode();
    
    configurationSubject.next(window["environment"]);
    window["environment"] = undefined;
    platformBrowserDynamic().bootstrapModule(AppModule)
    .catch(err => console.log(err));
  );

--- 环境解析器.ts

为了冗余,我使用 window["environment"] 为 BehaviorSubject 赋值。您可以设计一个解决方案,其中您的配置已经预加载并且 window["environment"] 在运行任何 Angular 的应用程序代码时已经填充,包括 main.ts 中的代码

import  BehaviorSubject  from "rxjs";
import  IConfig  from "../config.interface";

const config = <IConfig>Object.assign(, window["environment"]);
export const configurationSubject = new BehaviorSubject<IConfig>(config);
export function resolveEnvironment() 
  const env = configurationSubject.getValue().environment;
  let resolvedEnvironment = "";
  switch (env) 
 // case statements for determining whether this is dev, test, stage, or prod
  
  return resolvedEnvironment;


export function resolveNgxLoggerConfig() 
  return configurationSubject.getValue().logging;

-- app.module.ts - 精简以便于理解

有趣的事实!旧版本的 NGXLogger 要求您将对象传入 LoggerModule.forRoot()。事实上,LoggerModule 仍然可以! NGXLogger 友好地公开了 LoggerConfig,您可以覆盖它,允许您使用工厂函数进行设置。

import  resolveEnvironment, resolveNgxLoggerConfig, resolveSomethingElse  from './environment-resolvers';
import  LoggerConfig  from 'ngx-logger';
@NgModule(
    modules: [
        SomeModule.forRoot(resolveSomethingElse)
    ],
    providers:[
        
            provide: ENVIRONMENT,
            useFactory: resolveEnvironment
        ,
         
            provide: LoggerConfig,
            useFactory: resolveNgxLoggerConfig
        
    ]
)
export class AppModule

附录

我是如何解决创建 API 网址的问题的?

我希望能够通过评论了解每个 url 的作用,并希望进行类型检查,因为与 javascript (IMO) 相比,这是 TypeScript 的最大优势。我还想为其他开发人员创造一种体验,以添加新的端点和尽可能无缝的 api。

我创建了一个接受环境(dev、test、stage、prod、“”等)的类,并将这个值传递给一系列类[1-N],它们的工作是为每个 API 集合。每个 ApiCollection 负责为每个 API 集合创建基本 url。可以是我们自己的 API、供应商的 API,甚至是外部链接。该类会将创建的基本 url 传递到它包含的每个后续 api。阅读下面的代码以查看一个简单的示例。设置完成后,其他开发人员可以非常简单地向 Api 类添加另一个端点,而无需接触其他任何东西。

TLDR;内存优化的基本 OOP 原则和惰性获取器

@Injectable(
    providedIn: 'root'
)
export class ApiConfig 
    public apis: Apis;

    constructor(@Inject(ENVIRONMENT) private environment: string) 
        this.apis = new Apis(environment);
    


export class Apis 
    readonly microservices: MicroserviceApiCollection;

    constructor(environment: string) 
        this.microservices = new MicroserviceApiCollection(environment);
    


export abstract class ApiCollection 
  protected domain: any;

  constructor(environment: string) 
      const domain = this.resolveDomain(environment);
      Object.defineProperty(ApiCollection.prototype, 'domain', 
          get() 
              Object.defineProperty(this, 'domain',  value: domain );
              return this.domain;
          ,
          configurable: true
      );
  


export class MicroserviceApiCollection extends ApiCollection 
  public member: MemberApi;

  constructor(environment) 
      super(environment);
      this.member = new MemberApi(this.domain);
  

  resolveDomain(environment: string): string 
      return `https://subdomain$environment.actualdomain.com/`;
  


export class Api 
  readonly base: any;

  constructor(baseUrl: string) 
      Object.defineProperty(this, 'base', 
          get() 
              Object.defineProperty(this, 'base',
               value: baseUrl, configurable: true);
              return this.base;
          ,
          enumerable: false,
          configurable: true
      );
  

  attachProperty(name: string, value: any, enumerable?: boolean) 
      Object.defineProperty(this, name,
       value, writable: false, configurable: true, enumerable: enumerable || true );
  


export class MemberApi extends Api 

  /**
  * This comment will show up when referencing this.apiConfig.apis.microservices.member.memberInfo
  */
  get MemberInfo() 
    this.attachProperty("MemberInfo", `$this.basebasic-info`);
    return this.MemberInfo;
  

  constructor(baseUrl: string) 
    super(baseUrl + "member/api/");
  

【讨论】:

以上是关于App.settings - Angular 方式?的主要内容,如果未能解决你的问题,请参考以下文章

C#/ ASP.Net:app.settings而不是web.config

未知命令:shell_plus 和 --settings

angular组件间共享数据的四种方式

Angular1组件通讯方式总结

Angular 2 Reactive Forms 问题如何以角度方式而不是 jquery 方式进行

如何通过 Angular 方式示例在 Angular DataTables 中使用服务器端选项?