【问题标题】:Angular "APP_INITIALIZER" to have service without promise in angular 4?Angular“APP_INITIALIZER”在 Angular 4 中提供没有承诺的服务?
【发布时间】:2018-02-18 19:10:49
【问题描述】:

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

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

下面是代码:

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

感谢任何帮助。

提前致谢。

【问题讨论】:

  • 如果您使用的是 Angular 4,为什么这个问题会有 angular5 标签?无论如何,应该使用angular 标签,除非您确定问题是特定于版本的。

标签: angular typescript rxjs observable angular5


【解决方案1】:

tService.getToken 未定义,因为 DI 出错了,tService 实际上是 ConfigurationService[ConfigurationService, tokenService]注解表示会注入2个依赖,而工厂函数只有1个参数。

如果不使用ConfigurationService,则不必注入。

getToken 已经返回了一个 observable。 APP_INITIALIZER 期望异步初始化的承诺,因此应将可观察对象转换为承诺:

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

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

ConfigurationService 的问题在于它是异步的,但它只公开了 getConfigurationData 在某些时候可用的承诺的结果。多次调用getConfiguration 将导致重复请求。它应该公开一个可以轻松链接的 promise 或 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 可以在任何地方链接,并且不限于承诺控制流:

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

【讨论】:

  • 谢谢!如何让 configurationService 先执行,再执行 token 服务?
  • 有没有办法让 configService 在调用 tService.getToken() 之前执行一个函数?
  • 什么是configService?您的代码不包含 configurationService,但与此处相关。
  • ConfigurationService 基本上是从 app.config 文件中读取配置。所以我希望首先执行该服务。我怎样才能做到这一点?我将在问题中添加上面的配置部分。
  • 什么是getConfigurationData?它返回什么?是异步的吗?这才是最重要的。
猜你喜欢
  • 1970-01-01
  • 2018-08-16
  • 2018-01-16
  • 2018-07-05
  • 2015-08-12
  • 2017-11-11
  • 2017-03-01
  • 2019-10-21
  • 1970-01-01
相关资源
最近更新 更多