How to inject application config into app module in angular

9.7k Views Asked by At

I am using Angular v16 and I have the following config that needs to be included in app.module. What is the best way to inject appConfig in app.module? It works if I include them directly inside app.module providers, but I want to manage all the dependencies in a separate config file.

Note: I'm not using standalone component for app.component.ts. So I can't use bootstrapApplication to to inject them

app.config.ts

export const appConfig: ApplicationConfig = {
   providers: [
     provideAnimations(),
     provideHttpClient(),
     provideRouter(
        appRoutes,
        withInMemoryScrolling({scrollPositionRestoration: 'enabled'}),
     ),
   ]
}

main.ts

platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .catch((err) => console.error(err));
3

There are 3 best solutions below

2
Francesco Moro On

You should just import your constant into app.module.ts.

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

And then use it like this:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { appConfig } from './app.config';

@NgModule({
  ...appConfig
})
export class AppModule { }

If you want to define all the app metadata into your constant (imports, providers, exports, etc...) Make sure your AppConfig interface matches the one required info NgModule decorator one (more info here.)

0
Eli Porush On

If I understand you right - this is the suitable place to use forRoot() static method concept, see this example:

import { NgModule } from '@angular/core';
import { AppConfigService } from './app-config.service';

@NgModule({
  providers: [AppConfigService],
})
export class AppModule{
  static forRoot(config: AppConfig): ModuleWithProviders<AppModule> {
    return {
      ngModule: AppModule,
      providers: [{ provide: AppConfig, useValue: config }],
    };
  }
} 

in this way, you can inject from the outside the configuration you need and it will be loaded into the module. to implement it, you simply do this:

platformBrowserDynamic()
.bootstrapModule(AppModule.forRoot(YOUR CONFIGURATION OBJECT))
.catch((err) => console.error(err));

let me know if it works for you :)

2
Bytech On

I remember having trouble with config not getting loaded in time, and solving it using a service and HttpBackend (it "dispatches requests directly to the backend, without going through the interceptor chain.")

ConfigService

export class ConfigService {

  private appConfig: Config

  // Using handler: HttpBackend in constructor, this way synced config init can use httpClient
  private httpBackEnd: HttpClient

  constructor(
    public handler: HttpBackend
  ) {
    this.httpBackEnd = new HttpClient(handler)
  }


  loadConfig() {
    return this.httpBackEnd.get<Config>('config.json')
      .toPromise()
      .then(
        (config: Config) => { this.appConfig = config },
        (err) => { console.log('Failed to load config.json. ' + err) }
      )
  }

  get params() {
    if (!this.appConfig) { throw Error('Trying to use config, but it was not yet loaded!') }
    return this.appConfig
  }
}

And then, in app.module, calling loadConfig in a provider:

@NgModule({
  declarations: [...],
  imports: [...],
  providers: [
    {
      provide: APP_INITIALIZER,
      multi: true,
      deps: [ConfigService],
      useFactory: (config: ConfigService) => {
        return () => {
          return config.loadConfig()
        }
      }
    }
  ]
})

Example of config.json

{
  "api": {
    "domain": "http://homestead.test/",
    "route": "/v4"
  },
  "logoutRedirectPath": "/user/login",
  "devMode": false
}

The properties defined inside config.json are then available everywhere I import ConfigService and accessing e.g. this.configService.params.api.domain.

The other advantage is, the config can be updated, and the app does not need to be recompiled to take effect.