Window undefined when used as a useValue provider with Angular 4 AoT

When Angular 4.0.2 app is compiled ahead of schedule 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 fine but results in what window

matters undefined

when it is typed as

constructor(@Inject(windowToken) window) {
   window.navigator...
}

      

Error while loading:

TypeError: Cannot read property "navigator" of undefined

Upon closer inspection, the auto-generated app.module.ngfactory.js appears to be 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 fine:

export function windowFactory() {
  return window;
}
export const windowProvider = { provide: windowToken, useFactory: windowFactory };

      

What exactly is wrong with using window

as a useValue

provider here? Is this a known trap? Is this limitation limited to all global or all vendors useValue

?

+3


source to share


2 answers


I had a similar problem but it was with SignalrWindow. The concept and the error were identical.

Then I 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 problem.

Basically, this is all done using the factory method instead of useValue in the providers. I'm not sure why this is a problem, but I know this approach solves the aot problem.

Steps to fix:

Create exported function



export function windowFactory(): any {
    return window;
}

      

Then, in the base module at @NgModule

the vendors, you can do this:

...
providers: [
    { provide: SignalrWindow, useFactory: windowFactory }
  ]
...

      

Basically, you can rename the methods as you like (so in your example that would be :)

export const windowProvider = { provide: windowToken, useFactory: windowFactory };

      

+2


source


During AoT compilation, the angular CLI statically parses the code, also generates the ngmodule.factory file. It sees "useValue" and checks for a static value and puts it in ngmodule.factory. This value is not available at compile time, so it leaves this value to the provider and hence it is returned as "undefined" when injected into a constuctor.

However, "useFactory" is provided for the same purpose, where you do not know its meaning until runtime.



Because of this, useValue will not work in your script, but "useFactory" will.

Bottom Line: When compiling AOT, use "useValue" only if you have a static value such as a constant or number. Otherwise use "useFactory" to return the computed runtime object / values.

+1


source







All Articles