I am trying to use a service authService
in my main.ts file that contains the platformBrowserDynamic.bootstrapModule(AppModule)
;
The service handles the users auth and token renewal, and we would like it to get the user token before the application is bootstrapped. This will avoid bootstrapping the app, redirecting to authentication webpage, then bootstrapping the app again.
Below is how we handled the problem for JIT compiler, and It worked. However, this does not work if you want to take advantage of AOT (we do).
// main.ts
... imports ...
let windw: Window = window;
let authService = windw['authService'] = windw['authService'] || new AuthService();
authService.userLoadededEvent
.subscribe(user => {
if (location.pathname.match(/silentrenew/)) {
authService.completeSilentRenew();
} else if (user && !user.expired) {
platformBrowserDynamic().bootstrapModule(AppModule(authService));
} else {
if (location.hash.match(/id_token/)) {
authService.completeSignIn()
} else {
if (location.pathname.length > 1) {
let deeplink = location.href.replace(baseHref, '');
localStorage.setItem('deeplink', deeplink);
}
authService.signIn();
}
}
});
// AppModule
export const AppModule = (authService: AuthService = null) => {
@NgModule({
declarations: [
AppComponent
],
imports: [...],
providers: [
{
provide: AuthService,
useValue: authService
}
],
bootstrap: [AppComponent]
})
class AppModule { };
return AppModule;
}
As stated earlier, this approach works for JIT compilation, but I would like the application to AOT compile. When I AOT compile with the above code, I get the following error from angular. Unhandled Promise rejection: No NgModule metadata found for 'AppModule'. ; Zone: <root> ; Task: Promise.then ; Value: Error: No NgModule metadata found for 'AppModule'.
Anyone know how I can fix this problem? AuthService needs to be a singleton, and needs to be accessed inside and outside the bootstrapped application.
Things I have tried.
I tried updating AppModule to not be a function, but just a class declaration with a provider that uses the AuthService from the window obj. However, angular doesn't seem to be able to access the window obj where the authService is attached. Which causes the AuthService to be undefined
e.g.
//AppModule
let windw: Window = window;
@NgModule({
declarations: [
AppComponent
],
imports: [...],
providers: [
{
provide: AuthService,
useValue: windw['authService'] // undefined
}
],
bootstrap: [AppComponent]
})
class AppModule { };
I also tried passing the the AuthService into the bootstrapModule()
method. This doesn't cause any bootstrap error, but then I get an Injector error that AuthService
is not provided. I admit, I am not completely sure what angular does with the providers property on the CompilerOptions input for bootstrapModule.
app.component.html:18 ERROR Error:
StaticInjectorError[AuthService]:
NullInjectorError: No provider for AuthService!
// main.ts
... imports ...
let windw: Window = window;
let authService = windw['authService'] = windw['authService'] || new AuthService();
authService.userLoadededEvent
.subscribe(user => {
if (location.pathname.match(/silentrenew/)) {
authService.completeSilentRenew();
} else if (user && !user.expired) {
platformBrowserDynamic().bootstrapModule(AppModule, {
providers: [{provide: AuthService, useValue: authService}]
});
} else {
if (location.hash.match(/id_token/)) {
authService.completeSignIn()
} else {
if (location.pathname.length > 1) {
let deeplink = location.href.replace(baseHref, '');
localStorage.setItem('deeplink', deeplink);
}
authService.signIn();
}
}
});
// AppModule
export const AppModule = (authService: AuthService = null) => {
@NgModule({
declarations: [
AppComponent
],
imports: [...],
providers: [],
bootstrap: [AppComponent]
})
class AppModule { };
return AppModule;
}
Thanks again for any help.
The fact that class instance should be accessed outside Angular application doesn't mean that it should be exposed to global scope. ES modules can naturally provide singletons because they are evaluated once:
default
module export will be a singleton, as long as non-Angular application is being built together with Angular application.Otherwise
window
should be used inside factory function:This is the limitation that is imposed by AOT because
window
doesn't exist at the time when the application is being compiled in Node environment.