Search code examples
javascriptangulartypescriptobservableswitchmap

How to use switchmap to avoid nested subscriptions in Angular?


I read a lot about switchmap and its purpose but I did not see a lot of examples when it comes to subscribing to the new data. So I use a nested subscription in my Angular project and I wanted to ask you how to use switchmap properly in my example to understand the concept better.

Here my nested subscription:

this.sharedSrv.postDetail.subscribe(async post => {
        if(post) {
            this.hasPost = true;
            this.post = post;
        }
        console.log(post);
        this.viewedMainComment = null;
        this.viewedSubComments = [];
        this.userSrv.getUserAsObservable().subscribe(user => {
            if(user){
                if(this.post.user.id == user.id) this.isOwnPost = true; 
                this.user = user;
                this.postsSrv.getPostInteraction(this.user.id, this.post.id, this.post.user.id).subscribe(interaction => {
                    this.hasSubscribed = interaction["hasSubscribed"];
                    this.wasLiked = interaction["wasLiked"];
                    this.wasDisliked = interaction["wasDisliked"];
                    this.hasSaved = interaction["hasSaved"];
                });
                console.log(user);
                this.isLoggedIn = true
            } else {
                this.isLoggedIn = false;
            }
        })
})

How would I use switchmap correctly here? Any help is appreciated.


Solution

  • There are multiple things to notice here

    1. Looks like the outer observable this.sharedSrv.postDetail and this.userSrv.getUserAsObservable() are unrelated. In that case you could also use RxJS forkJoin to trigger the observables in parallel. Since you've asked for switchMap, you could try the following
    import { of } from 'rxjs';
    import { switchMap } from 'rxjs/operators';
    
    this.sharedSrv.postDetail.pipe(
      switchMap(post => {
        if (post) {
          this.hasPost = true;
          this.post = post;
        }
        console.log(post);
        this.viewedMainComment = null;
        this.viewedSubComments = [];
        return this.userSrv.getUserAsObservable();
      }),
      switchMap(user => {
        if (user) {
          if (this.post.user.id == user.id) this.isOwnPost = true;
          this.user = user;
          console.log(user);
          return this.postsSrv.getPostInteraction(this.user.id, this.post.id, this.post.user.id);
        }
        return of(null); // <-- emit `null` if `user` is undefined
      })
    ).subscribe(
      interaction => {
        if(!!interaction) { // <-- only proceed if `interaction` is defined
          this.hasSubscribed = interaction["hasSubscribed"];
          this.wasLiked = interaction["wasLiked"];
          this.wasDisliked = interaction["wasDisliked"];
          this.hasSaved = interaction["hasSaved"];
          this.isLoggedIn = true;
        } else { // <-- set `isLoggedIn` to false if `user` was undefined
          this.isLoggedIn = false;
        }
      }
    );
    
    1. I assume you're converting the observable to a promise using async. First I'd recommend you not to mix observables and promises unless absolutely necessary. Second you could use RxJS from function to convert an observable to a promise.
    import { from, of } from 'rxjs';
    import { switchMap } from 'rxjs/operators';
    
    const obs$ = this.sharedSrv.postDetail.pipe(
      switchMap(post => {
        if (post) {
          this.hasPost = true;
          this.post = post;
        }
        console.log(post);
        this.viewedMainComment = null;
        this.viewedSubComments = [];
        return this.userSrv.getUserAsObservable();
      }),
      switchMap(user => {
        if (user) {
          if (this.post.user.id == user.id) this.isOwnPost = true;
          this.user = user;
          console.log(user);
          return this.postsSrv.getPostInteraction(this.user.id, this.post.id, this.post.user.id);
        }
        return of(null); // <-- emit `null` if `user` is undefined
      })
    );
    
    from(obs$).then(
      interaction => {
        ...
      }
    );