Richard Barraclough
Richard Barraclough

Reputation: 2964

APP_INITIALIZER doesn't return

I want to load app settings before the Angular application starts. I copied in the code from another project but it stopped working. What is wrong?

In app.module.ts:

providers: [
  {
    provide: APP_INITIALIZER,
    multi: true,
    deps: [AppSettingsService],
    useFactory: (appSettingsService: AppSettingsService) => {
      return () => {
        return appSettingsService.init();
      };
    }
  },
]

And here is the app-settings.service.ts

@Injectable({
  providedIn: 'root'
})
export class AppSettingsService {

  private appsettings: Appsettings;

  constructor(private http: HttpClient) { }

  init(): Promise<Appsettings> {
    console.warn('AppSettingsService.init'); // This appears in the console.
    return this.http.get<Appsettings>('appsettings.json').pipe(
      tap(response => {
        console.warn('AppSettingsService.init.tap'); // This does not appear in the console.
        this.appsettings = response;
      })
    ).toPromise();
  }

  get authority(): string {
    return this.appsettings.jwtBearerSettings.authority;
  }
}

class Appsettings {
  baseUri: string;
  angularClientApiURI: string;
  jwtBearerSettings: JwtBearerSettings;
}

class JwtBearerSettings {
  authority: string;
}

In the project from which this is copied I see in the browser tools a GET request for appsettings.json but not so in the project to which it has been copied.

Update

I have found that if I remove the AuthInterceptor from the porviders then it starts working.

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  constructor(private authService: AuthService) { }

  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    return next.handle(this.addAuthenticationToken(request));
  }

  addAuthenticationToken(request: HttpRequest<unknown>): HttpRequest<unknown> {
    const token = this.authService.access_token; // (The id_token also works but can be much larger because it contains more claims.)
    if (token) {
      const tokenReq: HttpRequest<any> = request.clone({
        setHeaders: {
          Authorization: `Bearer ${token}`
        }
      });
      return tokenReq;
    }
    else {
      return request;
    }
  }
}

Upvotes: 1

Views: 880

Answers (1)

Richard Barraclough
Richard Barraclough

Reputation: 2964

The problem seems to be that the APP_INITIALIZER is using HttpClient which depends on HTTP_INTERCEPTORS which thus creates a mutual dependency. cf. https://github.com/angular/angular/issues/26845

The solution is How to make an angular module to ignore http interceptor added in a core module

which looks like this, i.e., to use HttpBackend to construct a HttpClient instead of having HttpClient provided by the DI which uses the HTTP_INTERCEPTORS.

export class AppSettingsService {

  private httpClient: HttpClient;
  private appsettings: Appsettings;

  constructor(private httpBackend: HttpBackend) {
    this.httpClient = new HttpClient(httpBackend);
  }

Upvotes: 2

Related Questions