简体   繁体   中英

Angular: Load config file before sending http request within the APP_INITIALIZER

In my SPA, I'm using the APP_INITIALIZER token, which is supposed to perform the following in the given sequence. 1) Load the config file relevant to the environment (DEV, QA, PROD). 2) Use the API URL from the config file to make the HTTP request for the authentication token 3) Use the API URL from the config file and append the token to the HTTP request (this happens within the token interceptor) to load some other config data)

Earlier I had 2 and 3 implemented with the URL hard-coded, and then everything worked fine. When I moved the API Urls to the config and tried loading them from the config file, the token endpoint is executed before the config file request is resolved, therefore the url becomes undefined.

See the code below:

app.module.ts

export function initializeData(appConfig: AppConfig, authService: AuthService, appService: AppService, globalService: GlobalService) {
  return () => {
    try {
      return Promise.all([appConfig.load(), authService.authenticateClient(), appService.getClientConfig()]).then(() => {
        console.log('success')
        return Promise.resolve();
      }, (err) => {
        alert(err.error.error_description);
        return Promise.reject(err);
      });
    } catch (e) {
      alert(e.message);
      console.log(e);
    }

  }
}

@NgModule({
.............
.............
providers: [
    AppConfig,
    AuthService,
    {
      provide: APP_INITIALIZER,
      useFactory: initializeData,
      deps: [AppConfig, AuthService, TranslateService, AppService, GlobalService],
      multi: true
    },
    AppService
]
});

app.config.ts:

@Injectable()
export class AppConfig {
  static settings: IAppConfig;
  constructor(private http: HttpClient) { }
  load() {
    const jsonFile = `assets/config/config.${environment.name}.json`;

    return new Promise((resolve, reject) => {
      this.http.get(jsonFile).pipe(map((res: IAppConfig) => {
        AppConfig.settings = <IAppConfig>res;
        return AppConfig.settings;
      })).subscribe(() => {
        resolve();
        })
    }).catch ((response: any) => {
        console.log(`Could not load file '${jsonFile}': ${JSON.stringify(response)}`);
      });
  }
}

auth.service.ts (token endpoint):

authenticateClient(){
    let body = new HttpParams()
      .set('client_id', AppConfig.settings.apiServer.client_id)
      .set('grant_type', AppConfig.settings.apiServer.grant_type)
      .set('scope', AppConfig.settings.apiServer.scope);

    const httpOptions = {
      headers: new HttpHeaders({
        'Content-Type': 'application/x-www-form-urlencoded'
      })
    };

    let authGatewayUrl: string = AppConfig.settings.apiServer.tokenUrl + window.location.search;

    return  this.http.post<any>(authGatewayUrl, body, httpOptions).toPromise().then(
        data => {
          this.token.next(data); 
          return this.token;
        },
        error => {
          return Promise.reject(error);
        }
      );  
  }

Now, when I add breakpoints in app.config.ts and auth.service.ts, in the latter file, AppConfig.Settings.apiServer.tokenUrl get hit before the app config file is resolved. Hence it becomes undefined.

How can I resolve this? I know I should be using switchmap, but I'm not sure how to approach it.

Perhaps you can control the sequence in .then callback:

appConfig.load()
  .then(()=>
     Promise.all[
       authService.authenticateClient(),
       appService.getClientConfig()
     ]
     .then....)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM