使用ngrx在一个@Effect中构造多个HTTP调用的最干净方法是什么

时间:2019-03-08 18:17:50

标签: angular typescript rxjs ngrx rxjs-pipeable-operators

我正在使用ngrx,并将@Effect连接到LOAD_CONTRACT操作,该操作随后进行3个HTTP调用以获取数据。私有变量用于存储每个GET中的数据,因此最后可以使用包含3个检索到的对象的有效负载调用LoadContractSuccessAction。

下面的代码可以正常工作,错误处理也可以工作……但是我觉得可能存在一种更整齐或更更好的结构化方法。

我不知道是否所有的嵌套都是必​​要的,或者是否可以以某种方式将其弄平。我也不确定使用switchMap是否是最好的运算符。

对ngrx最佳实践有较深了解的人是否可以评论如何改进/简化以下内容??

private clientContractIds: IClientContractIds;
private contract: Contract;
private contractSummaryMonths: ContractSummaryMonth[];
private contractSummaryTotals: ContractSummaryTotals;

// Loads a contract and its summary months and totals.
@Effect()
loadContract$ = this.actions$.pipe(
  ofType(ContractActionTypes.LOAD_CONTRACT),
    map((action: IActionWithPayload<IClientContractIds>) => {
      this.clientContractIds = {
        client_id: action.payload.client_id,
        contract_id: action.payload.contract_id
      };
    }),

    // Get the contract.
    switchMap(() => {
      return this.contractService.getContract$(this.clientContractIds).pipe(
        map(contract => (this.contract = contract)),
        catchError(error => throwError(error)),

        // Get the summary months.
        switchMap(() => {
          return this.contractService
            .getContractSummaryMonths$(this.clientContractIds)
            .pipe(
              map(
                contractSummaryMonths =>
                  (this.contractSummaryMonths = contractSummaryMonths)
              ),
              catchError(error => throwError(error))
            );
        }),

        // Get the summary totals.
        switchMap(() => {
          return this.contractService
            .getContractSummaryTotals$(this.clientContractIds)
            .pipe(
              map(
                contractSummaryTotals =>
                  (this.contractSummaryTotals = contractSummaryTotals)
              ),
              catchError(error => throwError(error))
            );
        }),

        // Call the success action with the payload objects.
        switchMap(() => {
          return of(
            new LoadContractSuccessAction({
              contract: this.contract,
              contractSummaryMonths: this.contractSummaryMonths,
              contractSummryTotals: this.contractSummaryTotals
            })
          );
        })
    );
  }),
  catchError(error => {
    return of(new LoadContractFailAction(error));
  })
);

2 个答案:

答案 0 :(得分:1)

我要做的是让您的loadContract $效果在每个http调用中分配一个动作,而在执行您的http调用时每个动作分配一个效果。拆分通话可以更轻松地理解和调试。

它可能不是100%准确,但是可以给您一个大致的想法。

@Effect()
loadContract$ = this.actions$.pipe(
  ofType(ContractActionTypes.LOAD_CONTRACT),
    switchMap((action: IActionWithPayload<IClientContractIds>) => {
      this.clientContractIds = {
        client_id: action.payload.client_id,
        contract_id: action.payload.contract_id
      };
     return [new action1(this.clientContractIds), new action2(this.clientContractIds), new action3(this.clientContractIds), new action4(this.clientContractIds)]
    })
)

@Effect()
action1$ = this.actions$.pipe(
    ofType(ContractActionTypes.action1),
    switchMap((action: ACTION1) => {
        return this.contractService
            .getContractSummaryMonths$(action.payload.clientContractIds)
            .pipe(
                map(
                    contractSummaryMonths =>
                        (this.contractSummaryMonths = contractSummaryMonths)
                ),
                catchError(error => throwError(error))
            );
    }))

我保留了您的代码,但我没有分配私有变量,而是为将使用化简器设置商店中数据的每个http调用调度成功操作。

答案 1 :(得分:0)

我使用forkJoin进行了重构,更干净了...

  @Effect()
  loadContract$ = this.actions$.pipe(
    ofType(ContractActionTypes.LOAD_CONTRACT),
    map((action: IActionWithPayload<IClientContractIds>) => {
      return {
        client_id: action.payload.client_id,
        contract_id: action.payload.contract_id
      };
    }),
    switchMap((clientContractIds: IClientContractIds) => {
      return forkJoin(
        // Get the contract.
        this.contractService
          .getContract$(clientContractIds)
          .pipe(catchError(error => throwError(error))),
        // Get the contract summary months.
        this.contractService
          .getContractSummaryMonths$(clientContractIds)
          .pipe(catchError(error => throwError(error))),
        // Get the contract summary totals.
        this.contractService
          .getContractSummaryTotals$(clientContractIds)
          .pipe(catchError(error => throwError(error)))
      ).pipe(
        map(([contract, contractSummaryMonths, contractSummaryTotals]) => {
          return new LoadContractSuccessAction({
            contract,
            contractSummaryMonths,
            contractSummaryTotals
          });
        }),
        catchError(error => {
          return of(new LoadContractFailAction(error));
        })
      );
    })
  );