Configuring angular production files after build

1.6k Views Asked by At

I have an angular 9 project which is part of an application suite installer [Wix installer]. One of the settings used by the angular app is the address of API which it fetches its data from some configurable address. I know that I can have many angular environment files and simply use commands such as the followings:

environment.env1.ts

export const environment = {
  production: true,
  apiAddress: "http://apiAddress1/api/",
};


ng build --prod --configuration=env1    

or

environment.env2.ts

    export const environment = {
      production: true,
      apiAddress: "http://apiAddress2/api/",
    };

    ng build --prod --configuration=env2

This means for every potential API address I need to do a new build and run above command. How can I overcome above scenario and configure output binaries after the build?

Assuming there is no clear way of achieving the after-built configuration, can I do 'simple string replace' for the address of API in the generated main*.js files? Would there be any side effects?

2

There are 2 best solutions below

0
On

It is probably possible to perform a find and replace on the generated files after the build completes, but that just feels wrong.

Instead create a configuration.json file and put it in your assets folder. Then in your application, reference the file directly by using an import. You might have to enable this functionality by setting resolveJsonModule to true in your tsconfig.json.

Now, after the application is built, the value can be replaced in configuration.json without fear of unintended side effects by using simple techniques such as in NodeJs with fs.readFileSync(path) and fs.writeFileSync(path, JSON.stringify(data)).

Sample xyz.module.ts section

import dynamicConfig from '../assets/configuration.json';
@NgModule({
  providers: [
    {
      provide: API_BASE_URL,
      useValue: dynamicConfig.apiAddress
    }
  ]
})
export class XyzModule { }
2
On

You can outsource your api endpoint in a config file. And provide an AppInitializer to use your config file. Here is an example:

the loader function, which loads the config via http from /config/config.json and sets the api configuration:

export function loadConfig(http: HttpClient, config: ApiConfiguration): (() => Promise<boolean>) {
  return (): Promise<boolean> => {
    return new Promise<boolean>((resolve: (a: boolean) => void): void => {
      http.get('./config/config.json')
        .pipe(
          map((x: any) => {
            config.rootUrl = x.rootUrl + '/v2';
            resolve(true);
          }),
          catchError((x: { status: number }, caught: Observable<void>): ObservableInput<{}> => {
            // 404 local development, other errors are strange
            resolve(x.status === 404);
            return of({});
          })
        ).subscribe();
    });
  };
}

The json contains only one string named rootUrl.

And to use this function, before the app initializes, provide an AppInitializer in your app.module.ts:

providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: loadConfig,
      deps: [
        HttpClient,
        ApiConfiguration
      ],
      multi: true
    }

ApiConfiguration stores the api endpoint which is just a class with a default value (for devlopment):

@Injectable({
  providedIn: 'root',
})
export class ApiConfiguration {
  rootUrl: string = 'localhost:8080';
}

Just put a config file in your /dist folder and it should work :)