window не определено при использовании в качестве поставщика useValue с Angular 4 AoT

Когда приложение Angular 4.0.2 скомпилировано заранее, а провайдер определен как useValue

import { OpaqueToken, Provider } from '@angular/core';

export const windowToken = new OpaqueToken('window');
export const windowProvider = { provide: windowToken, useValue: window };

и используется как

@NgModule({ providers: [windowProvider], ... })
export class AppModule {}

он компилируется нормально, но приводит к тому, что window становится undefined при введении как

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

При загрузке вылетает ошибка:

TypeError: не удается прочитать свойство «навигатор» неопределенного

При ближайшем рассмотрении автоматически сгенерированного app.module.ngfactory.js оказывается, что это действительно 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;
        }
        ...

При использовании того же сервиса, что и useFactory, все ок:

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

Что именно не так с использованием window в качестве поставщика useValue здесь? Это известная ловушка? Применяется ли это ограничение ко всем глобальным или ко всем useValue провайдерам?


person Estus Flask    schedule 17.04.2017    source источник
comment
У меня была похожая проблема, и мне пришлось поставить private перед @Inject. В вашем случае я не уверен, что это вызывает проблему.   -  person unitario    schedule 17.04.2017
comment
@unitario, который не имеет отношения, поскольку он использует его непосредственно в конструкторе. private создает свойство, и размещение его перед @Inject является синтаксической ошибкой.   -  person Aluan Haddad    schedule 17.04.2017
comment
Кто может сказать. AOT — неопределенный язык. На данный момент вы не пишете ни TypeScript, ни JavaScript, так что вы находитесь на неизведанной территории.   -  person Aluan Haddad    schedule 17.04.2017
comment
Большое спасибо за обходной путь, такое же поведение с 4.1.2   -  person FabiF    schedule 11.05.2017


Ответы (2)


У меня была похожая проблема, но это было с SignalrWindow. Хотя концепция и ошибка идентичны.

Затем я нашел эту статью здесь (https://blog.sstorie.com/integrating-angular-2-and-signalr-part-2-of-2/), а внизу статьи были комментарии, которые помогли мне решить проблему.

По сути, это сводится к использованию фабричного метода вместо useValue в провайдерах. Я не уверен, почему это проблема, но я знаю, что этот подход решает проблему aot.

Действия по устранению:

Создайте функцию, которая экспортируется

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

Затем в основном модуле в @NgModule в провайдерах можно сделать так:

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

По сути, вы можете переименовывать методы как хотите (так, в вашем примере это будет:)

export const windowProvider = { provide: windowToken, useFactory: windowFactory };
person Newteq Developer    schedule 03.07.2017

Во время компиляции AoT angular CLI статически анализирует код и создает файл ngmodule.factory. Он видит «useValue», проверяет, доступно ли статическое значение, и помещает его в ngmodule.factory. Во время компиляции это значение недоступно, поэтому оно оставляет это значение поставщика и, следовательно, возвращается как «неопределенное» при введении в конструктор.

Однако «useFactory» предоставляется для той же цели, где вы не знаете свое значение до времени выполнения.

Из-за этого в вашем сценарии не работает useValue, а вот useFactory будет работать.

Итог: при компиляции AOT используйте «useValue», только если у вас есть статическое значение, такое как постоянная строка или число. В противном случае используйте «useFactory» для возврата вычисляемых объектов/значений во время выполнения.

person Ashish Patel    schedule 08.12.2017