Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

typescript - Angular load external configuration before AppModule loads

Consider the following scenario (Angular v7):

  1. Load configuration parameters (API server URL and Auth server URL) from an external endpoint (JSON), before the AppModule is loaded
  2. Pass configuration to AppModule (OAuth2 module)
  3. Compile the app with AOT

Point 2 is key here, and looks like this:

@NgModule({
  imports: [
    ...
    OAuthModule.forRoot({
      resourceServer: {
        allowedUrls: [API_SERVER_URL], // <== we need to set the value that we loaded from the external endpoint (JSON) here
        sendAccessToken: true
      }
    }),
    ...
  ],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
export class AppModule { }

What I've tried to far:

  • A solution with APP_INITIALIZER. This doesn't work, as the OAuthModule.forRoot() is triggered before the APP_INITIALIZER can download the external configuration JSON.
  • Load the config with an async function in the main.ts into the Angular environment variables, then bootstrap the AppModule. Also doesn't work due to the import { AppModule } from './app/app.module'; statement in main.ts, which causes the AppModule to load and fire OAuthModule.forRoot() before the external config is loaded (this comment confirms this behavior).
  • Load the AppModule dynamically in main.ts, so without the import statement on top. This is the StackBlitz example given in that comment. It works, but 1) breaks lazy loading WARNING in Lazy routes discovery is not enabled. and 2) doesn't work with AOT compiling. It does come very close to what I need.

Curious to hear if someone is aware of another method to get external configuration loaded before the AppModule loads.

StackBlitz for option 3 (Load the AppModule dynamically): https://stackblitz.com/edit/angular-n8hdty

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Angular documentation has a great chapter called NgModule FAQs which contains the following section:

What if two modules provide the same service?

...

If NgModule A provides a service for token 'X' and imports an NgModule B that also provides a service for token 'X', then NgModule A's service definition "wins".

In other words, you can override OAuthModuleConfig for your library in AppModule:

main.ts

(async () => {
  const response = await fetch('https://api.myjson.com/bins/lf0ns');
  const config = await response.json();

  environment['allowedUrls'] = config.apiBaseURL;

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

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { OAuthModule, OAuthModuleConfig } from 'angular-oauth2-oidc';
import { HttpClientModule } from '@angular/common/http';
import { environment } from '../environments/environment';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    OAuthModule.forRoot(),
  ],
  providers: [
    {
      provide: OAuthModuleConfig,
      useFactory: () => ({
        resourceServer: {
          allowedUrls: [environment['allowedUrls']],
          sendAccessToken: true
        }
      })
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

Note that we should also use useFactory instead of useValue so we don't depend on when AppModule is imported.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...