Welcome to OStack Knowledge Sharing 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 Answer

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...