TypeScript 在对象数组上使用 forkJoin

TypeScript using forkJoin on array of objects

假设我有一个 Web 服务,它将 return 基本数据如下(结果名为 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'
            }
        }
    }
}

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

{
    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 的描述,我错过了 mergeMap 来合并多个 Observable 的结果。我的最终来源如下:

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;
}

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

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

    });
  }

}