網站搜尋

typescript - Angular "APP_INITIALIZER" 在 Angular 4 中如何讓服務沒有 promise?


我有一個應用程式,它存在於父應用程式的 iframe 中。

當我的應用程式在 iFrame 中載入時,在我的應用程式的 AppModule 中,我有一個名為 tokenService 的 APP_INITIALIZER。這個服務會向父應用程式發送 window.sendMessage 以獲取 token。因此在 token 服務中有一個 "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;
    }
}

任何幫助都值得感激。

提前感謝。

答案#1

tService.getToken 未定義,因為依賴注入(DI)出錯,而 tService 實際上是 ConfigurationService。[ConfigurationService, tokenService] 註解表示將注入 2 個依賴,但工廠函數只有 1 個參數。

如果它沒有使用 ConfigurationService,就不必注入它。

getToken 已經返回一個可觀察對象。APP_INITIALIZER 期望一個用於非同步初始化的 promise,因此應將可觀察對象轉換為 promise:

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

和

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

ConfigurationService 的問題在於它是非同步的,但它只暴露那個承諾的結果,而該結果將在某個時候可以透過 getConfigurationData 獲取。多次呼叫 getConfiguration 將導致重複的請求。它應該暴露一個可以輕鬆鏈結的承諾或可觀察對象 (observable) :

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);
  }
}