Angular 2 and Team City Production/Environment Var

2019-01-19 08:07发布

问题:

We have a new Angular app that is building AOT to a folder. Everything is working fine, but we are trying to configure it to a five-step build process using TeamCity/Octopus Deploy, where each step will use some different variables for endpoints (API calls, etc.) I'm trying to figure out the best way to pass this to an AOT app that is not running off a backend.

I know about the --environment flag that can be used to fire off different configs, but our goal is to have one single build go through all the environments. I am at a loss for how to pass variables in the application based on the environment it's in.

My current idea is to leave a config.js file in the assets folder so the app can load it before the rest of the app and set some variables on the window, but that leaves me with the problem of not being able to import a TS file into the files that need those variables.

How can I pass this info into the app in a more intuitive way? Is this impossible to get around without separate builds?

回答1:

I would build on the config.js idea you have. The config should be loaded as part of the application startup, not part of the build. You need to make a service that loads the config.js on startup of your application, use the APP_INITIALIZER provider and pass it the factory that creates the service. Here is an example:

app.module.ts

import { NgModule, APP_INITIALIZER } from '@angular/core';

@NgModule({
    imports: [
        ....
    ],
    declarations: [
        ....
    ],
    providers: [
        {
            provide: APP_INITIALIZER,
            useFactory: configServiceFactory,
            deps: [ConfigService, Http, AppConfig],
            multi: true
        },
        AppConfig,
        ConfigService
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
}

Config Service:

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';

import { AppConfig } from '../../app.config';

@Injectable()
export class ConfigService {
    private _config: AppConfig;

    constructor(private http: Http, private config: AppConfig) {
    }

    public Load(): Promise<AppConfig> {
        return new Promise((resolve) => {
            this.http.get('./config.json').map(res => res.json())
            .subscribe((config: AppConfig) => {
                this.copyConfiguration(config, new AppConfig()).then((data: AppConfig) => {
                    this._config = data;
                    resolve(this._config);
                });
            }, (error: any) => {
                this._config = new AppConfig();
                resolve(this._config);
            });
        });
    }

    public GetApiUrl(endPoint: any): string {
        return `${this._config.ApiUrls.BaseUrl}/${this._config.ApiUrls[ endPoint ]}`;
    }

    public GetApiEndPoint(endPoint: any): string {
        return this._config.ApiUrls[ endPoint ];
    }

    public Get(key: any): any {
        return this._config[ key ];
    }

    private copyConfiguration(source: Object, destination: Object): any {
        return new Promise(function(resolve) {
            Object.keys(source).forEach(function(key) {
                destination[ key ] = source[ key ];
                resolve(destination);
            });
        });
    }
}

export function configServiceFactory(config: ConfigService) {
    return () => config.Load();
}