首页 > 解决方案 > 水龙头不等待角度后端服务调用的响应

问题描述

在服务中使用 tap 运算符时如何订阅响应。

有谁知道如何解决这个问题?

edit(status) {
    dataObj.val = status;
    // call post service with status..
    this.service
        .update(dataObj)
        .pipe(takeUntil(this._ngUnsubscribe$))
        .subscribe(() => {
            //i would like to wait until response come from backend and then navigate to the page so i                get data over there.
            if (res.status === 'Success') {
                this.router
                    .navigate(['../../success'], {
                        relativeTo: this.route,
                    })
                    .then(() => {});
            } else {
                this.location.back();
            }
        });
}

//akita store service

update(
    obj: any,
): Observable < any > {
    return this.service.update(obj).pipe(
        delay(800),
        map((data: RestfulResponse < any > ) => data.data),
        tap((data: anny) => {
            this.store.update((state) => {
                state.updateValue = data; // value is not updating and it is navigating to route
            });
        }),
    );
}

//post service

update(obj){
//post call
}

有什么方法可以在服务端使用点击并在组件端订阅?

我知道我可以使用 finalize ,但它对编写内部条件没有帮助。

标签: javascriptangulartypescriptrxjs

解决方案


按照tap设计,操作员可以处理在可观察管道的上下文中不会发生的副作用。这意味着您的管道永远不会等待tap自身的结果。我不建议以这种方式使用它。大多数情况下,我只tap用于调试。

如果您正在等待特定的状态更改,您应该创建一个单独的可观察对象,从您的商店中选择,以观察预期更改的状态。

如果您想在发生某些事情时触发其他操作,我建议使用 ngrx Effects来实现这一点。

看看这篇文章,我在其中谈到了如何实现类似的用例: https ://stackoverflow.com/a/64491398/166850

您还应该努力设置应用您的状态更改的减速器,而不是直接更新商店。

将以下每一项视为一个单独的关注点,您可以独立于其他关注点来实现:

  1. 当用户进行编辑时,触发编辑动作。
  2. reducer 应该根据编辑操作更新状态(例如,显示正在保存)
  3. 当编辑动作被触发时,触发一个效果。应用程序应进行 HTTP 调用以保存更改,然后触发保存完成操作。
  4. 保存完成后,应触发路由器导航。

这会将您的代码分成多个单元,这些单元易于独立测试和验证。

如果 #1 产生一个被你的 reducer (#2) 消耗的动作,你还可以为 #3 创建一个 ngrx 效果,它监听相同的动作,使用 处理 HTTP 调用switchMap,然后触发另一个动作来表示它已经完成。

编辑

这是一个简单的例子。第一次(从 AppComponent)触发名为 APP_LOADED 的动作时,此效果会进行 HTTP 调用以从服务器获取数据,然后使用响应数据作为动作负载触发动作。

实际的 HTTP 调用被委托给另一个服务,HttpMyConfigDataService,它只是调用HttpClient并返回一个Observable.

@Injectable({
  providedIn: 'root'
})
export class LoadMyConfigEffect {
  constructor(
    private httpMyConfigDataService: HttpMyConfigDataService,
    private action$: Actions
  ) {
  }

  loadMyConfigData$ = createEffect(() => {
    return this.action$.pipe(
      filter((action) => action.type === 'APP_LOADED'),
      take(1),
      switchMap(() => this.httpMyConfigDataService.get().pipe(
        map(data => {
          return {type: 'MY_CONFIG_DATA_LOADED', payload: data};
        }),
        catchError(err => {
          console.error('Error loading config data.', err);
          return of({type: 'CONFIG_LOAD_ERROR', payload: err.message, isError: true);
        })
      ))
    );
  });
}

推荐阅读