Angular 订阅外数据未定义

Angular data undefined outside subscribe

我知道它被问了一百万次,但我真的不明白它是如何工作的。我尝试了新的 Promise,但里面什么都没有 then() 我试图用 mergeMap 来做,但搞砸了。 我想获得当前用户最喜欢的项目。为此,我调用了这个方法:

user: IUser;
tracks: ITrack[] = [];

public getFavorites() {
    this.userService.getFavourite(this.user.id).subscribe(
      (tracks: ILike[]) => {
        if (tracks) {
          tracks.forEach(track => {
            this.tracks.push(this.loadTracks(track.id));
          });
        }
      }
    );

对于这种方法,我需要我得到的用户 ID 和 :

private async loadUser() {
    this.accountService.currentUser$.subscribe((user: IUser) => {
      // getting into another service, because currentUser don't have id
      this.userService.getUserByUsername(user.username).subscribe((u: IUser) => {
        this.user = u;
      })
    })
  }

还有 track_id:

loadTracks(id: number) {
    let track: ITrack;
    this.trackService.getTrack(id).subscribe(t => track = t);
    return track;
}

ngOnInit():

this.loadUser();
this.getFavorites();

如何正确使用 mergeMap?

我想你会想在这种情况下使用 switchMap

this.accountService.currentUser$.pipe(
    switchMap((user: IUser)=> {
       return this.userService.getUserByUsername(user.username);
    }),
    tap(user => {
       console.log(user); // Check if its work
    }),
    switchMap(user => {
       return this.userService.getFavourite(this.user.id)
    }),
    tap(tracks => {
       console.log(tracks); // Now you have track
    })
); // You can .subscribe() here or use async pipe

您也可以拆分此逻辑并重复使用

const getUser$ = this.accountService.currentUser$.pipe(
    switchMap((user: IUser)=> {
       return this.userService.getUserByUsername(user.username);
    })
);

阅读更多关于高阶可观察量的信息here

您可能正在寻找 switchMap 而不是 mergeMapmergeMap 当你想合并 observables 的结果时应该使用。

这是一个工作示例: https://stackblitz.com/edit/typescript-sdf2gy?file=index.ts

let userLocation$ = of({ userId: 123 }).pipe(delay(2000));
let getFavourite = function (userId) {
  return of({ someData: 'someData: ' + userId }).pipe(delay(1000));
};

userLocation$
  .pipe(switchMap((data) => getFavourite(data.userId)))
  .subscribe((v) => console.log(v));

如果将所有内容都保持为可观察的,您可能会发现更容易理解(直到您确实需要订阅)。我们可以将每条数据声明为一个可观察源,然后从另一个定义一个可观察到的。

user$: Observable<IUser> = this.accountService.currentUser$.pipe(
    switchMap(user => this.userService.getUserByUsername(user.username))
);

tracks$: Observable<ITrack[]> = this.user$.pipe(
    switchMap(user => this.userService.getFavourite(user.id)),
    switchMap(tracks => forkJoin(
        tracks.map(t => this.trackService.getTrack(t.id))
    ))
);

现在,只需订阅 tracks$ 即可获取曲目列表。不需要额外的变量来存储数据,不需要调用“加载”方法,甚至不需要使用 ngOnit.

注意 tracks$ 的定义是如何以 user$ 开头的。这清楚地表明,当 user$ 发出新值(accountService.currentUser$ 更改 )时,tracks$ 将自动发出更新的曲目列表。

注意:您可能会发现 有帮助,因为问题涉及类似的情况并且答案有非常 in-depth 的解释。