问题描述
假设我们有一个组件,其方法执行服务中定义的 http 请求。我们订阅了组件中某处的 observable(也可以在模板中):
组件:
getData() {
this.apiService.getDataFromServer().subscribe(
a => console.log(a),b => console.error(b)
);
}
API 服务:
getDataFromServer() {
return this.http.get();
}
当请求失败时,我想打开一个向用户显示错误的模式。在此模式中,用户找到两个按钮:一个表示“重新加载”,另一个表示“取消”。如果单击其中一个按钮,模态将再次关闭。
现在有一种(便宜的)方法来处理这个问题,就是从组件中的错误情况再次调用 getData()
。当然,在服务级别处理错误会好得多。这就是我在这里努力实现的目标。
我现在想弄清楚如何处理按钮点击。使用 pipe
和 catchError
,不可能在服务方法中“等待”重新加载按钮点击。我认为处理这个问题的好方法是 retrywhen
,但我不知道该怎么做。
这是我扩展 API 服务的尝试:
// The modal with the two buttons will call
// - this.apiService.button$.next(true) if the user wants to reload,or
// - this.apiService.button$.next(false) if the user does not want to reload
button$ = new Subject<boolean>();
getDataFromServer() {
return this.http.get().pipe(
retrywhen((errors: Observable<any>) => {
this.openErrorModal(); // this is a simple method that shows the modal to the user
// how to use the button$ subject here?
// I tried with
// - return this.button$.pipe(),and
// - return errors.pipe(),// but I was not able to finalize it to make it work
}
);
}
openErrorModal() {
// Open a modal with a simple GUI
}
最后,我的目标是组件在不知道请求完成的频率的情况下获取数据或错误。基本上,每次请求失败时都会打开错误模式,因此用户可以根据需要将自己保持在无限循环中。只要他点击“取消”,就不会再有请求了。
注意:我的问题被大大简化了。我试图删除所有与问题无关的代码。
解决方法
您可以稍微更改设计以获得所需的行为。在中间添加一个主题(而不是从 getDataFromServer
函数返回可观察对象)。
这样,您可以在出现故障时重试服务,如果您将其作为服务的基本功能实现,则可以用它包装每个调用并避免代码重复。
它看起来像:
tempSubject: Subject<any>;
getDataFromServer() {
this.tempSubject = new Subject();
this.getDataFromServerInternal(this.tempSubject);
return this.tempSubject;
}
private getDataFromServerInternal(sub: Subject < any > ) {
this.httpClient
.get()
.subscribe(
(res) => sub.next(res),(error) => {
const confirmed = confirm(`try again? error: ${error}`);
if (confirmed) {
this.getDataFromServerInternal(sub);
}
}
);
}
您应该记住在完成后完成并取消订阅所有内容(以避免内存泄漏)。来自组件的调用看起来一样