7

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));
1
  • How did you solve this issue? I got a similar problem. Commented Oct 17, 2023 at 9:34

4 Answers 4

4

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.

Sign up to request clarification or add additional context in comments.

2 Comments

I load my configs from a web API service but had the same issue with it being too late for my Angular components due to the async nature of the web calls. Loading the configs from the web API only once for the app also makes sense. Your approach with the APP_INITIALIZER solved it for me as well. Nice post!
Hey, awesome to hear it helped! =D
1

import {bootstrapApplication} from '@angular/platform-browser';
import {appConfig} from './app/app.config';
import {AppComponent} from './app/app.component';

bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));

You can pass your config like this.

Comments

0

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 :)

Comments

-1

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.)

2 Comments

its not working. I tried that before. It's only working in following way but I have to optimize it for non-standalone bootstrapApplication(AppComponent, appConfig).catch(err => console.error(err));
This is just pasting the config object directly into app.module. It wont work and is same as writing config URL into app.module itself.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.