Most modern way to externalize Angular 2 environme

2020-02-01 03:58发布

问题:

I'm curious what the most modern way, as of April 2017, to build an Angular 2 app for deployment with external configuration?

Following the Build Once Deploy Anywhere philosophy I'd like to figure out how to package my Angular 2 app as a Docker image that I can drop onto any server and configure from outside.

I've done this fairly simple with most other components of this application including a Spring Boot backend, but the best way to do this with Angular 2 is hard to pin down since the framework evolved so massively between beta releases that a lot of information is out-of-date.

I've got Angular 2.4.9 with NPM for package management running in a Node Docker container as a JIT app with specific environment.ts baked into the image at build-time. I'd like to get the size and speed advantages of AoT compilation as well as eventual minification and other download size enhancements, but AoT bakes the contents of an environment.ts file directly into main.bundle.js so there's no way at all to change it after ng build

The Angular2-webpack-starter project seems very out-of-date and is mutually exclusive with Angular-CLI anyway, but it's got this method here which seems to setup config/webpack.ENV.js files that refer to OS environment variables. I feel like environment variables are a bit overly complicated for a lot of apps, but it's better than nothing. But how can I apply this in Angular-CLI terms, or is there a better way?

As I understand it Angular-CLI abstracts webpack too heavily to directly access the webpack and plugin configs to follow this approach. But could I just reference process.env in my environment.ts and Bob's your uncle or is it more complicated than that?

Do I need to crack open main.bundle.js and rewrite the bits between var environment = { and //# sourceMappingURL=environment.js.map? That seems needlessly hacky for something as popular as Angular.

回答1:

If you really must build once and deploy the same build artifact multiple times then one solution (although in my opinion is a bit of a hack) is to put your external configuration inside 'assets' folder then make an ajax call from environment.ts to read the values:

src/environments/environment.ts:

export const environment = new Promise((resolve, reject) => {
  var xhr = new XMLHttpRequest();
  xhr.open('GET', './assets/environment.json');
  xhr.onload = function () {
    if (xhr.status === 200) {
      resolve(JSON.parse(xhr.responseText));
    }
    else {
      reject("Cannot load configuration...");
    }
  };
  xhr.send();
});

src/assets/environment.json:

{
  "production": false,
  "some_api_url": "https://dev_url"
}

You also need to postpone module bootstrapping to when ajax call is completed:

src/main.ts:

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment as environmentPromise } from './environments/environment';

environmentPromise.then(environment => {

  if (environment["production"]) {
    enableProdMode();
  }

  platformBrowserDynamic().bootstrapModule(AppModule);
});

Working sample here: https://github.com/mehradoo/angular-external-cfg/commit/0dd6122a0d8ff100c23458807cc379e9e24cc439