typescript - Angular "APP_INITIALIZER" för att ha service utan promise i Angular 4?
Jag har en app som finns i en iframe i föräldraapplikationen.
När min app som är inom iFrame laddas, i mitt applikations AppModule, har jag en APP_INITIALIZER som heter tokenService. Den här tjänsten skickar ett window.sendMessage till huvudapplikationen för att få en token. Så det finns en "message"-händelsehanterare i tokentjänsten.
Nedan är koden:
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);
};
}
Metoden "processMessage" kallas när resultatet kommer tillbaka.
„tokenService“ har satts som en „APP_INITIALIZER“. Nedan är koden:
{
'provide': APP_INITIALIZER,
'useFactory': loadService,
'deps': [ConfigurationService, tokenService],
'multi': true,
},
configService initieras också:
export function loadConfig(config: ConfigurationService): Function {
return () => config.configuration$;
}
{
'provide': APP_INITIALIZER,
'useFactory': loadConfig,
'deps': [ConfigurationService],
'multi': true,
}
I filen app.module.ts finns det en metod:
export function loadService(tService: tokenService): Function {
return () => tService.getToken();
}
Jag är inte säker på hur man gör denna händelsehanterare: "processMessage" som en promise-metod. Kan någon hjälpa mig med det? För jag får ett fel när jag försöker köra applikationen. Felet är:
ERROR TypeError: tService.getToken is not a function
at Array.eval (app.module.ts:44)
at ApplicationInitStatus.runInitializers (core.js:3569)
Jag vill också att tokenService ska slutföra sin körning innan andra komponenter i min applikation initialiseras. Hur kan jag se till att tokenService har slutfört körningen och att händelsehanteraren för sendMessage har kallats innan appen fortsätter att ladda andra komponenter?
Koden för konfigurationstjänsten är nedan:
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;
}
}
all hjälp uppskattas.
Tack i förväg.
Svar #1
tService.getToken är odefinierad eftersom DI gick fel, och tService är faktiskt ConfigurationService. [ConfigurationService, tokenService]-annoteringen betyder att 2 beroenden kommer att injiceras, medan fabriksfunktionen bara har 1 parameter.
Om det inte använder ConfigurationService behöver det inte injiceras.
getToken returnerar redan ett observable. APP_INITIALIZER förväntar sig ett promise för asynkrona initialiseringar, så ett observable bör konverteras till ett promise:
'deps': [tokenService],
'multi': true,
},
och
export function loadService(tService: tokenService): Function {
return () => tService.getToken().toPromise();
}
Problemet med ConfigurationService är att det är asynkront men det exponerar endast resultatet av det löftet som kommer att vara tillgängligt med getConfigurationData vid någon tidpunkt. Att kalla getConfiguration flera gånger kommer att resultera i återkommande förfrågningar. Det borde exponera ett löfte eller ett observerbart objekt som enkelt kan kedjas:
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())
}
}
Då kan configurationPromise kedjas var som helst och det är inte begränsat till promise-styrning:
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);
}
}
