Search code examples
angularglobal-variablesangular7environmentproduction

Problem with Loading Global Variable in Production Build In Angular Project


Trying to load environment from js file to having dynamic environment, independent from build based on this article.

env.js file added to project like this:

(function (window) {
  window.__env = window.__env || {};

  // API url
  window.__env.apiUrl = 'http://dev.your-api.com';

  // Whether or not to enable debug mode
  // Setting this to false will disable console output
  window.__env.enableDebug = true;
}(this));

Then i add script to index.html to load js file:

<script src="env.js"></script>

I use global variable this way:

console.log(window['__env']);

I serve the project everything works fine, I Build the project (regular build with ng build) everything is fine too,

But when i build with ng build --prod, the global variable is null,

how to tell webpack to there is a global variable that you should brig it in without using services?

any idea?


Solution

  • I use a JSON file to read Environment dynamically build independent:

    This line Load env.json in AppModule inside APP_Initilizer:

    export function init_app(appLoadService: AppInitService) {
      return () => appLoadService.init("assets/env.json");
    } 
    

    Note: dont forget the provide part:

      providers: [
        EnvServiceProvider,
        AppInitService,
        AuthenticationService,
        // TabGuard,
        {
          provide: APP_INITIALIZER,
          useFactory: init_app,
          deps: [AppInitService],
          multi: true,
        }
        // ...
        ]
    

    The AppInitService:

    @Injectable({
      providedIn: 'root'
    })
    export class AppInitService {
    
      // This is the method you want to call at bootstrap
      // Important: It should return a Promise
      public init(path:string) {
        return from(
            fetch(path).then(function(response) {
              return response.json();
            })
          ).pipe(
            map((config) => {
            window['__env'] = config;
            return
          })).toPromise();
      }
    } 
    

    Then use an environment provider to provide envs:

    import { EnvService } from './env.service';
    
    export const EnvServiceFactory = () => {
      // Create env
      const env = new EnvService();
    
      // Read environment variables from browser window
      const browserWindow = window || {};
      const browserWindowEnv = browserWindow['__env'] || {};
    
      // Assign environment variables from browser window to env
      // In the current implementation, properties from env.js overwrite defaults from the EnvService.
      // If needed, a deep merge can be performed here to merge properties instead of overwriting them.
      for (const key in browserWindowEnv) {
        if (browserWindowEnv.hasOwnProperty(key)) {
          env[key] = window['__env'][key];
        }
      }
      return env;
    };
    
    export const EnvServiceProvider = {
      provide: EnvService,
      useFactory: EnvServiceFactory,
      deps: [],
    };
    

    Use env provider like this:

      env: any;
      constructor(
        public envService: EnvService
      ) {
        this.env = this.envService;
      }