Angular provider: Why is my useFactory method not being called?

Viewed 1205

-The issue- I am attempting to load a config file containing some environment to connect to an Okta app. the method being passed into APP_INITIALIZER's useFactory: works just fine, but the method being passed into OKTA_CONFIG's useFactory: is never even being called.

I placed some console.log() messages within the methods to see what I could glean, and here's what I got in the console:

loadConfig called!
core.js:6228 ERROR Error: Your Okta URL is missing...

the log message from configService.oktaConfig() appears to not be happening.

-The code-

app.module.ts

    import { BrowserModule } from '@angular/platform-browser';
    import { APP_INITIALIZER, NgModule } from '@angular/core';
    import { MaterialModule } from './modules/material/material.module';
    import { AppRoutingModule } from './app-routing.module';
    import { AppComponent } from './app.component';
    import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
    import { OKTA_CONFIG, OktaAuthModule } from '@okta/okta-angular';
    import { HttpClientModule } from '@angular/common/http';
    import { ConfigService } from './services/config.service';
    
    @NgModule({
      declarations: [
        ...
      ],
      imports: [
        BrowserModule,
        OktaAuthModule,
        AppRoutingModule,
        BrowserAnimationsModule,
        MaterialModule,
        FormsModule,
        HttpClientModule
      ],
      providers: [
        { provide: APP_INITIALIZER, useFactory: appInitializerFactory, deps:[ConfigService], multi: true},
        { provide: OKTA_CONFIG, useFactory: oktaInitializerFactory, deps:[ConfigService], multi: false}
      ],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    
    export function appInitializerFactory(configService: ConfigService) {
      return () => configService.loadConfig();
    }
    
    export function oktaInitializerFactory(configService: ConfigService) {
      return () => configService.oktaConfig();
    }

config.service.ts

    import { Injectable } from '@angular/core';
    import { HttpClient, HttpBackend } from '@angular/common/http';
    
    export interface IOktaConfig {
      pkce: boolean,
      issuer: string,
      redirectUri: string,
      clientId: string
    }
    
    export interface IAppConfig {
      production: boolean
      oidc: IOktaConfig
    }
    
    @Injectable({
      providedIn: 'root'
    })
    
    export class ConfigService {
      private httpClient: HttpClient;
      private config: IAppConfig;
    
      constructor(handler: HttpBackend) {
        this.httpClient = new HttpClient(handler);
      }
    
      public oktaConfig() {
        console.log("oktaConfig called!")!
        return this.config.oidc;
      }
    
      public async loadConfig(): Promise<any> {
        return this.httpClient.get('assets/config.json').pipe(settings => settings)
          .toPromise()
          .then(settings => {
            this.config = settings as IAppConfig;
            console.log("loadConfig called!");
          });
      }
    }
1 Answers

Return the actual data on oktaInitializerFactory instead of a function

export function oktaInitializerFactory(configService: ConfigService) {
   return configService.oktaConfig();
}

I have provided a Stackblitz Demo based on what I have understood on the scenario you had written above, with the provided solution, Let me know if I have missed something.

Related