【问题标题】:TypeScript using forkJoin on array of objectsTypeScript 在对象数组上使用 forkJoin
【发布时间】:2018-01-02 18:12:22
【问题描述】:

假设我有一个 Web 服务,它将返回如下基本数据(结果名为 CustomerGroups):

{
    0: {
        Name: 'Group 1',
        Customers: {
            0: {
                CustomerNo: 1,
                Name: 'Customer 1'
            },
            1: {
                CustomerNo: 2,
                Name: 'Customer 2'
            }
        }
    },
    1: {
        Name: 'Group 2',
        Customers: {
            0: {
                CustomerNo: 3,
                Name: 'Customer 3'
            },
            1: {
                CustomerNo: 4,
                Name: 'Customer 4'
            }
        }
    }
}

此外,我还有一个可以返回详细客户数据的网络服务:

{
    CustomerNo: 1,
    Street: 'Test Street 123',
    PostCode: '99999',
    City: 'Blabla',
    ...
}

我想要做的是在 Angular4 可注入服务中使用 forkJoin 组合这两个服务的结果。但是当我尝试请求给定客户的每个详细信息时,我卡住了:

ReadAll(useCache?: boolean): Observable<ICustomerGroup[]> {
    if (!this.customerGroupCache || !useCache) {
        return this.http.get(this.urlGetAll)
            .map((res: Response) => res.json())
            .flatMap((customerGroups: any[]) => {
                if (customerGroups.length > 0) {
                    return Observable.forkJoin(
                        customerGroups.map((customerGroup: any) => {
                            return this.customerService.get(/* any CustomerNo of customerGroup.Customers */)
                            // ^---- This is my problem!!!
                        })
                    );
                }
                // No data! Return empty array
                return Observable.of([]);
            })
            .catch((error: any) => Observable.throw(error.message || 'Server error'));
    }

    return Observable.of(this.customerGroupCache);
}

如何使用forkJoin 循环每个CustomerGroup(flatMap 曾经这样做)并获取每个Customer 的详细信息?是否可以在forkJoin 中使用forEach

forkJoin 的结果应如下所示:

{
    0: {
        Name: 'Group 1',
        Customers: {
            0: {
                CustomerNo: 1,
                Name: 'Customer 1',
                Street: 'Test Street 123',
                PostCode: '99999',
                City: 'Blabla'
            },
            1: {
                CustomerNo: 2,
                Name: 'Customer 2',
                Street: 'Test Street 456',
                PostCode: '888',
                City: 'Blabla'
            }
        }
    }
    ...
}

解决方案

根据 taras-d 的描述,我错过了合并多个 Observable 的结果的 mergeMap。我的最终来源如下:

ReadAll(useCache?: boolean): Observable<ICustomerGroup[]> {
    if (!this.customerGroupCache || !useCache) {
        return this.http.get(this.urlGetAll)
            .mergeMap((res: Response) => {
                const customerObservables = [];
                let groups = res.json();

                groups.forEach((group, i) => {
                    group.Customers.forEach((cust, j) => {
                        customerObservables.push(this.customerService.get(cust.CustomerNo));
                    });
                });

                return Observable.forkJoin(customerObservables)
                    .map(customers => {
                        this.customerGroupCache = this.buildCustomerGroupArray(groups, customers);
                        return this.customerGroupCache;
                    });
            });
    }

    return Observable.of(this.customerGroupCache);
}

把它们放在一起:

private buildCustomerGroupArray(allGroups: any, allCustomers: any): Array<ICustomerGroup> {
    let result: Array<ICustomerGroup> = Array<ICustomerGroup>();
    allGroups.forEach((group, index) => {
        let newGroup = new CustomerGroup();
        newGroup.ActionFlag = ActionType.Undefined;
        newGroup.Name = group.Name;
        newGroup.OldName = group.OldName;
        newGroup.CustomerList = Array<ICustomerGroupItem>();

        group.Customers.forEach((cust, index2) => {
            if (allCustomers.find(p => p.CustomerNo === cust.CustomerNo)) {
                let currCust = allCustomers.find(p => p.CustomerNo === cust.CustomerNo);
                let newGroupItem: ICustomerGroupItem = new CustomerGroupItem({
                    ActionFlag: ActionType.Undefined,
                    CustomerName: currCust.Name,
                    CustomerNo: currCust.CustomerNo
                });

                newGroup.CustomerList.push(newGroupItem);
            }
        });

        result.push(newGroup);
    });

    return result;
}

【问题讨论】:

    标签: json angular web-services typescript fork-join


    【解决方案1】:

    最简单的方法是获取所有组和所有客户,然后将它们组合起来。

    export class AppComponent {
    
      // Fake customer service
      customerService = {
        getGroups() {
          return Observable.of({
              0: {
                  Name: 'Group 1',
                  Customers: {
                      0: {
                          CustomerNo: 1,
                          Name: 'Customer 1'
                      },
                      1: {
                          CustomerNo: 2,
                          Name: 'Customer 2'
                      }
                  }
              },
              1: {
                  Name: 'Group 2',
                  Customers: {
                      0: {
                          CustomerNo: 3,
                          Name: 'Customer 3'
                      },
                      1: {
                          CustomerNo: 4,
                          Name: 'Customer 4'
                      }
                  }
              }
          });
        },
        getCustomer(num) {
          return Observable.of({
              CustomerNo: num,
              Street: `Street ${num}`,
              PostCode: `PostCode ${num}`,
              City: `City ${num}`
          });
        }
      };
    
      readAll(): Observable<any> {
    
        // Get all groups
        return this.customerService.getGroups().mergeMap(allGroups => {
    
          const customersObservables = [];
    
          // Loop over groups
          for (let groupNum in allGroups) {
            const group = allGroups[groupNum];
    
            // Loop over customers in group
            for (let customerNum in group.Customers) {
              const customer = group.Customers[customerNum];
    
              // Create observable for every group customer
              customersObservables.push(
                this.customerService.getCustomer(customer.CustomerNo)
              );
            }
          }
    
          // Join all customers observable and map (return) all groups and all customers
          return Observable.forkJoin(customersObservables)
            .map(allCustomers => [allGroups, allCustomers]);
        });
      }
    
      ngOnInit(): void {
        this.readAll().subscribe(res => {
    
          // Here you will receive all groups and all customers
          const [ allGroups, allCustomers ] = res;
    
          console.log( JSON.stringify(allGroups, null, 2) );
          console.log( JSON.stringify(allCustomers, null, 2) )
    
          // TODO: Combine result
    
        });
      }
    
    }
    

    【讨论】:

    • BTW in RxJS 5 flatMapmergeMap 的别名
    • 为了清理 forkjoin 的所有 subsriptions 我需要在 forkJoin(customersObservables).pipe(takeUntil(this.unsubscribe$))this.customerService.getCustomer(customer.CustomerNo).pipe(takeUntil(this.unsubscribe$)) 调用 takeUntil
    • @dewey 我认为这两种变体都是正确的
    猜你喜欢
    • 1970-01-01
    • 2021-06-09
    • 2019-02-25
    • 2022-01-05
    • 2019-05-01
    • 2018-11-12
    • 2021-01-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多