Skip to content
Snippets Groups Projects
multi-environments-docker-build-for-angular.md 2.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • # Multi-environment docker build for Angular app
    
    As we use docker to deploy our application on different environments, we didn't want to build one image per environment but rather have one image representing a version of the application taking a dynamic configuration.
    
    The default Angular environment system doesn't allow that as the env file is compiled and so has to be defined at build time.
    
    To address this issue we implemented an alternative system that reads the configuration from a config.json file located in the assets folder. This happens at the bootstrap of the application in the user browser.
    
    Here is the corresponding code.
    
    At the bootstrap of the app we fetch the config.json file and set a window variable with the retrieved content.
    
    ```
    // main.ts
    
    fetch('./assets/config/config.json')
      .then(response => response.json())
      .then((config) => {
        if (!config) {
          return;
        }
    
        // Store the response so that your ConfigService can read it.
        window['adminGuiEnvConfig'] = config;
    
        platformBrowserDynamic().bootstrapModule(AppModule)
          .catch(err => console.log(err));
    
      });
    ```
    
    Then we add a dedicated service `app-config.service.ts` which is added to the `providers` in the app module.
    
    ```
    // This class describes the configuration of the project
    export class AppConfig {
      organizations: {
        url: string;
      };
      ...
    }
    
    // Export the varaible that contains the configuration 
    export let APP_CONFIG: AppConfig;
    
    @Injectable()
    export class AppConfigService {
    
      constructor() { }
    
      // Method that reads the window variable containing the configuration and initialize
      // an AppConfig object contained in the export APP_CONFIG variable
      public load() {
        return new Promise((resolve, reject) => {
          const conf = new AppConfig();
          APP_CONFIG = Object.assign(conf, window['adminGuiEnvConfig']);
          resolve();
        });
      }
    }
    ```
    
    Add an APP_INITIALIZER that will call the load method when the application starts and so on initialize the configuration.
    
    ```
    // app.module.ts
    
    export function initAppConfig(appConfigService: AppConfigService) {
      return (): Promise<any> => {
        return new Promise((resolve, reject) => {
          appConfigService.load();
          resolve();
        });
      };
    }
    
    @NgModule({
      ...
      providers: [
        AppConfigService,
        {
          provide: APP_INITIALIZER,
          useFactory: initAppConfig,
          deps: [AppConfigService],
          multi: true,
        },
      ],
      bootstrap: [AppComponent],
    })
    ```
    
    Import and use the APP_CONFIG in your components, services... when you need it.
    
    ```
    // Example
    import { APP_CONFIG } from './app-config.service';
    
    @Injectable()
    export class OrganizationService {
    
      organizationServiceUrl: string;
    
      private _searchChangeSubject: Subject<any>;
    
      constructor() {
        this.organizationServiceUrl = `${APP_CONFIG.organizations.url}organizations/`;
      }
    }
    
    ```