我期待Angular等到我的loadConfig()
函数在构造其他服务之前解析,但事实并非如此。
app.module.ts
export function initializeConfig(config: AppConfig){
return () => config.loadConfig();
}
@NgModule({
declarations: [...]
providers: [
AppConfig,
{ provide: APP_INITIALIZER, useFactory: initializeConfig, deps: [AppConfig], multi: true }
] })
export class AppModule {
}
app.config.ts
@Injectable()
export class AppConfig {
config: any;
constructor(
private injector: Injector
){
}
public loadConfig() {
const http = this.injector.get(HttpClient);
return new Promise((resolve, reject) => {
http.get('http://mycoolapp.com/env')
.map((res) => res )
.catch((err) => {
console.log("ERROR getting config data", err );
resolve(true);
return Observable.throw(err || 'Server error while getting environment');
})
.subscribe( (configData) => {
console.log("configData: ", configData);
this.config = configData;
resolve(true);
});
});
}
}
一些-另一service.ts
@Injectable()
export class SomeOtherService {
constructor(
private appConfig: AppConfig
) {
console.log("This is getting called before appConfig's loadConfig method is resolved!");
}
}
在从服务器接收数据之前调用SomeOtherService
的构造函数。这是一个问题,因为SomeOtherService
中的字段没有设置为正确的值。
如何确保在SomeOtherService
的请求解决后才调用loadConfig
的构造函数?
答案 0 :(得分:5)
我还有一个类似的问题,解决了我的问题是使用Observable方法和运算符来做所有事情。然后,最后使用toPromise
的{{1}}方法返回Observable
。这也更简单,因为您不需要自己创建承诺。
Promise
服务将看起来像这样:
AppConfig
我在rxjs中使用了新的pipeable operators,这是Google为Angular 5推荐的。import { Injectable, Injector } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { tap } from 'rxjs/operators/tap';
@Injectable()
export class AppConfig {
config: any = null;
constructor(
private injector: Injector
){
}
public loadConfig() {
const http = this.injector.get(HttpClient);
return http.get('https://jsonplaceholder.typicode.com/posts/1').pipe(
tap((returnedConfig) => this.config = returnedConfig)
).toPromise();
//return from([1]).toPromise();
}
}
运算符等同于旧的tap
运算符。
我还在stackblitz.com上创建了一个工作示例,以便您可以使用它。 Sample link
答案 1 :(得分:4)
Injector不会等待可观察量或承诺,也没有代码可以实现它。
您应该使用自定义Guard或Resolver来确保在初始导航完成之前加载配置。
答案 2 :(得分:2)
async loadConfig() {
const http = this.injector.get(HttpClient);
const configData = await http.get('http://mycoolapp.com/env')
.map((res: Response) => {
return res.json();
}).catch((err: any) => {
return Observable.throw(err);
}).toPromise();
this.config = configData;
});
}
await运算符用于等待Promise。它只能在异步函数中使用。
工作正常。
答案 3 :(得分:1)
首先,你真的很接近正确的解决方案!
但在我解释之前,请允许我告诉您,在服务中使用<?xml version="1.0" encoding="UTF-8"?> <facelet-taglib
...
<function>
<function-name>createArray</function-name>
<function-class>package.UIHelper</function-class>
<function-signature>Object[] createArray(int)</function-signature>
</function> </facelet-taglib>
通常会产生代码异味。
也就是说,如果您查看APP_INITALIZER source code,它只是在所有可用的初始化程序上运行subscribe
。 Promise.all本身等待所有承诺在继续之前完成,因此,如果你希望Angular在引导应用程序之前等待它,你应该从你的函数返回一个promise。
所以@AlesD的answer绝对是正确的方法。
(我只是想解释一下为什么)
我最近在我的一个项目中完成了这样的重构(使用Promise.all
),如果需要,可以查看PR here。
现在,如果我必须重写你的代码,我会这样做:
app.module.ts
APP_INITALIZER
app.config.ts;
export function initializeConfig(config: AppConfig) {
return () => config.loadConfig().toPromise();
}
@NgModule({
declarations: [
// ...
],
providers: [
HttpClientModule,
AppConfig,
{
provide: APP_INITIALIZER,
useFactory: initializeConfig,
deps: [AppConfig, HttpClientModule],
multi: true,
},
],
})
export class AppModule {}
答案 4 :(得分:1)
我认为您不应该订阅http get调用,而是在解析loadConfig promise之前将其转换为promise,因为可以在请求返回之前调用subscribe的回调,因此可以提前解析。尝试:
@Injectable()
export class AppConfig {
config: any;
constructor(
private injector: Injector
){
}
public loadConfig() {
const http = this.injector.get(HttpClient);
return new Promise((resolve, reject) => {
http.get('http://mycoolapp.com/env')
.map((res) => res )
.toPromise()
.catch((err) => {
console.log("ERROR getting config data", err );
resolve(true);
return Observable.throw(err || 'Server error while getting environment');
})
.then( (configData) => {
console.log("configData: ", configData);
this.config = configData;
resolve(true);
});
});
}
}
我只是在超时时尝试过,但这很有效。我希望toPromise()
处于正确的位置,因为我并没有真正使用地图功能。
答案 5 :(得分:0)
我正面临类似的问题。我认为未在此处宣布的差异导致在其他答案示例中效果很好,但不适用于作者,因此注入SomeOtherService。如果将其注入到其他服务中,则可能无法解析初始化程序。我认为初始化器将延迟将服务注入组件而不是其他服务,这将解释其在其他答案中的作用。就我而言,由于https://github.com/ngrx/platform/issues/931
,我遇到了此问题