【问题标题】:How to query nested data from database using multiple API calls in Angular?如何在 Angular 中使用多个 API 调用从数据库中查询嵌套数据?
【发布时间】:2021-10-21 12:27:37
【问题描述】:

我正在学习 Angular 和 API 服务。目前,我使用router 重定向到使用group id 的组详细信息页面

这是我想要实现的目标: 在详细页面中,我想在角度页面中显示用户名用户城市,这需要多次API调用(可能需要按顺序)才能从中获取数据group data

  1. 我需要使用route id/group id 通过getGroup() 获取组对象
  2. 每个组包含多个用户 ID
  3. (获取用户名)我需要迭代每个 用户 ID 以获取包含 用户名城市 IDuser object getUser()
  4. (获取城市名称)我需要遍历每个 city Ids 以获取包含 city idscity namescity object 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"
    }
   ]
}

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

## 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>

【问题讨论】:

    标签: angular typescript async-await rxjs pipe


    【解决方案1】:

    我不建议您单独为每个用户和城市提出请求。该操作将花费很长时间,因为您将为一页渲染发出许多 http 请求。您可以从数据库中获取所有城市和所有用户并在本地进行过滤。或者,您可以让 API 在单个请求中返回所有需要的数据。但是,如果这些选项都不适合您,您可以链接 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>
    

    【讨论】:

    • 我同意你的意见!我确实有服务可以一次获得所有用户和城市。例如,getAllUsers()getAllCities()。要根据您的专业知识改进代码,您介意根据我当前的代码 (this.route.params.pipe(switchMap((params) =&gt; this.groupService.getGroup(params.id))) 修改您的答案吗?
    • 好吧,在 html 上,我想你想显示用户名和城市名。但你改变你想要的方式
    【解决方案2】:

    链接请求可以解决问题,它会像这样:

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

    希望这会有所帮助。

    【讨论】:

    • 嗨 Mohamed,getUser()getCity 使用 user_idscity_ids,而不是 params.id
    【解决方案3】:

    这是关于 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);
    }
    

    【讨论】:

      猜你喜欢
      • 2017-11-01
      • 2018-08-23
      • 1970-01-01
      • 2019-11-23
      • 1970-01-01
      • 2021-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多