Angular "APP_INITIALIZER" 没有承诺在 angular 4 中提供服务?

Angular "APP_INITIALIZER" to have service without promise in angular 4?

我有一个应用程序存在于父应用程序的 iframe 中。

当我在 iFrame 中的应用程序加载时,在我的应用程序 AppModule 中,我有一个名为 tokenService 的 APP_INITIALIZER。此服务向父应用程序发送 window.sendMessage 以获取令牌。所以令牌服务中有一个"message"事件处理程序。

代码如下:

    import { Injectable } from '@angular/core';
import { ConfigurationService } from './configService';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class tokenService {

  private _configs;
  private msgId = this.newId();
  private messageToGetToken = {
    'id': this.msgId,
    'type': 'test/V/GetToken',
    'data': null
  };

  constructor(private configService: ConfigurationService) {
    this._configs = configService.getConfigurationData();
  }

  getToken() {
    if (this._configs.loginRequired == true) {
      if (window.addEventListener) {
        window.addEventListener('message', this.processMessage, false);
      }
      else {
        (<any>window).attachEvent("onmessage", this.processMessage);
      }

      parent.window.postMessage(JSON.stringify(this.messageToGetToken), '*');

      return Observable.fromEvent(window, 'message')
        .subscribe((messageEvent: MessageEvent) => { this.processMessage(messageEvent); });
    }
  }

  private processMessage(evt) {
    var result = JSON.parse(evt);
    if (result && result.responseFor && result.responseFor === this.msgId) {
      localStorage.setItem('token', result.data ? result.data[0] : null);
      console.log(result.data);
    }
    console.log(evt);
  }

  private newId() {
    return '_' + Math.random().toString(36).substr(2, 9);
  };
}

方法 "processMessage" 在结果返回时被调用。

"tokenService" 已设置为 "APP_INITIALIZER"。下面是代码:

{
      'provide': APP_INITIALIZER,
      'useFactory': loadService,
      'deps': [ConfigurationService, tokenService],
      'multi': true,
    },

configService 也已初始化:

export function loadConfig(config: ConfigurationService): Function {
  return () => config.configuration$;
}
{
      'provide': APP_INITIALIZER,
      'useFactory': loadConfig,
      'deps': [ConfigurationService],
      'multi': true,
}

在app.module.ts文件中,有方法:

export function loadService(tService: tokenService): Function {
  return () => tService.getToken();
}

我不确定如何将此事件处理程序:"processMessage" 作为 promise 方法。任何人都可以帮我吗? 因为当我尝试 运行 应用程序时出现错误。错误是:

ERROR TypeError: tService.getToken is not a function
    at Array.eval (app.module.ts:44)
    at ApplicationInitStatus.runInitializers (core.js:3569)

另外,我想让这个 tokenService 在我的应用程序中的其他组件初始化之前完成它的执行。在应用程序继续加载其他组件之前,如何确保 tokenService 已完成执行并调用了 sendMessage 的事件处理程序?

配置服务代码如下:

    import { Http } from '@angular/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';

import 'rxjs/Rx';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';
import 'rxjs/add/operator/catch';

@Injectable()
export class ConfigurationService {
  private configuration;
    constructor(private http: Http) {
    }

    getConfiguration(): Promise<any> {
      let ret = this.http.get('appConfig.json').map(
        res => this.configuration = res.json())
        .toPromise()
        .then((data: any) => {
          this.configuration = data;
        })
        .catch((err: any) => {
          console.log("error while reading app config!");
        });

      return ret.then((x) => {
      });
    }

    getConfigurationData(): any {
      return this.configuration;
    }
}

感谢任何帮助。

提前致谢。

tService.getToken是未定义的,因为DI出错了,tService其实是ConfigurationService[ConfigurationService, tokenService]注解意味着将注入2个依赖,而工厂函数只有1个参数。

如果不用ConfigurationService,则不用注入

getToken 已经 returns 可观察。 APP_INITIALIZER 需要异步初始化的承诺,因此应将可观察对象转换为承诺:

'deps': [tokenService],
  'multi': true,
},

export function loadService(tService: tokenService): Function {
  return () => tService.getToken().toPromise();
}

ConfigurationService 的问题在于它是异步的,但它只公开了 getConfigurationData 在某个时候可用的承诺的结果。多次调用 getConfiguration 将导致重复请求。它应该公开一个可以轻松链接的承诺或可观察对象:

export class ConfigurationService {
  public configurationPromise = this.getConfiguration().toPromise();
  public configuration;

  constructor(private http: Http) {
    this.configurationPromise.then(configuration => {
      this.configuration = configuration;
    });
  }

  private getConfiguration(): Observable<any> {
    return this.http.get('appConfig.json').map(res => res.json())
  }
}

然后 configurationPromise 可以链接到任何地方,并且不限于 promise 控制流:

export class tokenService {
  ...
  constructor(private configService: ConfigurationService) {}

  getToken(): Observable<any> {
    ...
    return Observable.fromPromise(configService.configurationPromise)
    .switchMap(() => Observable.fromEvent(window, 'message'))
    .map((messageEvent: MessageEvent) => this.processMessage(messageEvent))
    .take(1);
  }
}