如何在 Angular 中使用多个 API 调用从数据库中查询嵌套数据?

How to query nested data from database using multiple API calls in Angular?

我正在学习angular和API服务。目前,我使用 router 重定向到使用 group id

的组详细信息页面

这是我想要实现的目标: 在详情页面,我想在angular页面显示用户名用户城市,需要多个API 调用(可能需要按顺序)从 group data.

获取数据
  1. 我需要使用 route id/group id 通过 getGroup()
  2. 获取组对象
  3. 每个组包含多个用户Id
  4. (获取用户名)我需要迭代每个 用户 ID 以获得 user object 其中包含 用户名 城市 ID 来自 getUser()
  5. (获取城市名称)我需要迭代每个 city IDs 以获得 city object 其中包含 city ids城市名称 来自 getCity()

所有数据概览:

######### group data
{
  "groups": [
    {
      "id": "group_id1",
      "users": ["user_id1", "user_id2"]
    },
    {
      "id": "group_id2",
      "users": ["user_id3", "user_id4"]
    }
   ]
}

######### user data
{
  "users": [
    {
      "id": "user_id1",
      "city": ["city_id1", "city_id2"]
    },
    {
      "id": "user_id2",
      "city": ["city_id3", "city_id4"]
    },
    {
      "id": "user_id3",
      "city": ["city_id1", "city_id3"]
    },
    {
      "id": "user_id4",
      "city": ["city_id2", "city_id4"]
    }
   ]
}

######### city data
{
  "cities": [
    {
      "id": "city_id1",
      "name": "NY"
    }
    {
      "id": "city_id2",
      "name": "BOS"
    }
    {
      "id": "city_id3",
      "name": "SF"
    }
    {
      "id": "city_id4",
      "name": "LA"
    }
   ]
}

目前,我可以获取组,但我不知道如何获取用户名和城市。

## component.ts
export class displayComponent implements OnInit {
  group$!: Observable<group>;

  constructor(
    private router: Router,
    private route: ActivatedRoute,
    private groupService: GroupService,
    private userService: UserService,
    private cityService: CityService,
  ) {}

  ngOnInit(): void {
    this.group$ = this.route.params.pipe(
      switchMap((params) => this.groupService.getGroup(params.id))
    );

// how to iterate all **user Id** to get user names via `getUser(**user Id**)`
// how to iterate all **city Id** to get city names via `getCity(**city Id**)`
  }

## html
<div *ngIf="group$ | async as group">
  <p> {{ group.id }} </p>
 <!-- user names placeholder-->
 <!-- user city names placeholder-->
</div>

链接请求就可以了,它会像这样:

Your_Source.pipe(
      switchMap((params) => this.groupService.getGroup(params.id))
      switchMap((params) => this.userService.getUser(params.id))
      switchMap((params) => this.cityService.getCity(params.id))
    );

希望对您有所帮助。

是关于RxJs的。应该是这样的。虽然未测试

private getCitiesInfo() {
 this.http.get('getGroupByIdApi')
  .pipe(
    map(group => groups.users),
    switchMap(userIds => this.constructGetUsersRequest(userIds)),
    map(usersData => usersData.flat().map(user => user.city)),
    switchMap(cityIds => this.constructGetCityRequests(cityIds))
  ).subscribe();
}


private constructGetUsersRequest(userIds: string[]): Observable<User>[] {
    const requests: Observable<User>[] = userIds.map(userId => this.http.get('userIdGetApi'));
    return combineLatest(requests);
}

private constructGetCityRequests(cityIds: string[]): Observable<City>[] {
    const requests: Observable<City>[]  = cityIds.map(cityId => this.http.get('cityIdGetApi'));
    return combineLatest(requests);
}

我不建议您分别为每个用户和城市提出请求。该操作将永远持续下去,因为您将为一个页面呈现发出许多 http 请求。您可以从数据库中获取所有城市和所有用户,然后在本地进行过滤。或者您可以在单个请求中生成 API return 所有需要的数据。但是,如果这些选项都不适合您,您可以链接 switchMap 管道操作。例如:

switchMap(group => {
  let observable = of({});

  group.users.forEach(userId => {
    observable = observable.pipe(
      switchMap(() => {
        return this.userService.getUser(userId);
      }),
      tap(user => {
        ...
      })
    );
  });

  return observable;
})

编辑:这段代码展示了如何通过首先获取所有城市和用户来实现。

## component.ts
export class displayComponent implements OnInit {
  userMap: { [userId: string]: User };
  cityMap: { [cityId: string]: City };
  group: Group;

  constructor(
    private router: Router,
    private route: ActivatedRoute,
    private groupService: GroupService,
    private userService: UserService,
    private cityService: CityService,
  ) {}

  ngOnInit(): void {
    this.userMap = {};
    this.cityMap = {};

    this.userService.getAllUsers().pipe(
      switchMap(users => {
        users.forEach(user => {
          this.userMap[user.id] = user;
        })
        return this.cityService.getAllCities();
      }),
      switchMap(cities => {
        cities.forEach(city => {
          this.cityMap[city.id] = city;
        });

        return this.route.params;
      }),
      switchMap(params => {
        return this.groupService.getGroup(params.id);
      })
    ).subscribe(group => {
      this.group = group;
    });
  }

## html
<div *ngIf="group">
  <p> {{ group.id }} </p>
  <ul>
    <li *ngFor="let userId of group.users">
      {{ userMap[userId].name }}<br>
      <div class="city" *ngFor="let cityId of userMap[userId].city">
       {{ cityMap[cityId].name }}
      </div>
    </li>
  </ul>
</div>