When Angular 4.0.2 application is compiled ahead-of-time, and the provider is defined as useValue
import { OpaqueToken, Provider } from '@angular/core';
export const windowToken = new OpaqueToken('window');
export const windowProvider = { provide: windowToken, useValue: window };
and used like
@NgModule({ providers: [windowProvider], ... })
export class AppModule {}
it compiles ok but results in window
being undefined
when injected as
constructor(@Inject(windowToken) window) {
window.navigator...
}
The error is thrown on bootstrapping:
TypeError: Cannot read property 'navigator' of undefined
On a closer look at auto-generated app.module.ngfactory.js it appears that it is indeed undefined
:
...
import * as import39 from './window';
var AppModuleInjector = (function (_super) {
...
AppModuleInjector.prototype.createInternal = function () {
...
this._windowToken_26 = undefined;
this._SomeService_27 = new import16.SomeService(this._windowToken_26);
}
AppModuleInjector.prototype.getInternal = function (token, notFoundResult) {
...
if ((token === import39.windowToken)) {
return this._windowToken_26;
}
...
When the same service is used as useFactory
, everything is ok:
export function windowFactory() {
return window;
}
export const windowProvider = { provide: windowToken, useFactory: windowFactory };
What exactly is wrong with using window
as useValue
provider here? Is it a known pitfall? Does this limitation apply to all globals or all useValue
providers?
I was having a similar problem but it was with a SignalrWindow. The concept and the error though was identical.
I then found this article here (https://blog.sstorie.com/integrating-angular-2-and-signalr-part-2-of-2/), and there were some comments at the bottom of the article that helped me solve the problem.
Basically, it boils done to using a factory method instead of a useValue in the providers. I'm not sure why it's a problem, but I do know that this approach solves the aot problem.
The steps to fix:
Create a function that is exported
export function windowFactory(): any {
return window;
}
Then in the core module, in @NgModule
in the providers you can do this:
...
providers: [
{ provide: SignalrWindow, useFactory: windowFactory }
]
...
Basically, you can rename the methods however you like (so, in your example it would be:)
export const windowProvider = { provide: windowToken, useFactory: windowFactory };