My Angular 4 / TypeScript 2.3服务有一个函数build()
,如果未初始化类属性,则会出错。我正在尝试构建一个更安全的版本 - safeBuild()
- 它将返回一个Observable,它将等待并在尝试调用build()
之前监听要初始化的属性
export class BuildService {
renderer:Renderer2; // must be set for build() below to work
// emits the new Renderer2 when renderer is set
private rendererSet$:BehaviorSubject<Renderer2> = new BehaviorSubject(null);
/** Set renderer, and notify any listener */
setRenderer(renderer:Renderer2){
this.renderer = renderer;
this.rendererSet$.next(renderer);
}
/** Returns a new DOM element. Requires renderer to be set */
build(elemTag:string){
// if renderer is not set, we can't proceed
// why is this error thrown when safeBuild() is called?
if (!this.renderer)
throw new Error('Renderer must be set before build() is run');
return this.renderer.createElement(elemTag);
}
/**
* A safe version of build(). Will wait until renderer is set
* before attempting to call build (Asynchronous)
*/
safeBuild(elemTag:string):Observable<any> {
// inform user that renderer should be set
// this warning is printed to the console as expected
if (!this.renderer)
console.warn('The build will be delayed until setRenderer() is called');
// Listen to rendererSet$, filter out the null output, and call build()
// only once the renderer is set. Why does the error still get thrown?
return Observable.concat(
this.rendererSet$.filter(e=>!!e).take(1),
Observable.of(this.build(elemTag))
)
}
}
我尝试像这样构建(来自其他服务):
this.buildService.safeBuild(elemTag).subscribe(...)
在控制台中我看到:
警告:构建将被延迟,直到调用setRenderer()
错误:必须在运行build()之前设置渲染器
我预计会发出警告,但在应用程序的另一部分调用setRenderer()
之前不会发生任何事情。此时,subscribe()
中的代码将会运行。
为什么我会看到错误?
答案 0 :(得分:3)
问题是在编写this.build(elemTag)
observable时调用concat
- 而不是在执行连接时调用。{/ p>
您可以使用defer
解决问题:
import 'rxjs/add/observable/defer';
...
return Observable.concat(
this.rendererSet$.filter(e => !!e).take(1),
Observable.defer(() => Observable.of(this.build(elemTag)))
);
或者,正如评论中指出的那样,使用map
:
return this.rendererSet$
.filter(e => !!e)
.take(1)
.map(() => this.build(elemTag));
答案 1 :(得分:-1)
这是因为,您创建了一个任何this.build
函数返回的Observable。因为,您还没有设置渲染器,下面的行会引发错误。确保你
首先调用setRenderer
函数
if (!this.renderer)
throw new Error('Renderer must be set before build() is run');
你应该能够通过返回一个Observable来解决这个问题,如下所示
return Observable.concat(
this.rendererSet$.filter(e=>!!e).take(1),
this.rendererSet$.asObservable().map(() => this.build(elemTag)) // this line will execute when there is a new value set to rendererSet
)