1

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.

Opat
  • 93
  • 1
  • 2
  • 9
  • While the question is valid, it is the preferred practice to remove all the unrelated statements from the code, as they obfuscate the essentials. In your case, about 2/3 of the code can be removed from the question. – Avius Oct 25 '20 at 11:19
  • @Avius I am sorry, I did not remove code so that you see what I am trying here and maybe you see other mistakes I do and correct me. – Opat Oct 25 '20 at 11:48
  • Fair enough and no problem (no need to apologise), just something to keep in mind in the future :] – Avius Oct 25 '20 at 17:11

3 Answers3

3

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 => {
    ...
  }
);
ruth
  • 29,535
  • 4
  • 30
  • 57
1

Looks like what you want:

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();
  }),
  swtichMap(user => {
    this.isLoggedIn = false;
    if (user) {
      if (this.post.user.id == user.id) this.isOwnPost = true;
      this.user = user;
      this.isLoggedIn = true;
      console.log(user);
      return this.postsSrv.getPostInteraction(this.user.id, this.post.id, this.post.user.id);
    } else {
      return of(null);
    }
  }).subscribe(interaction => {
    if (!interaction) return;
    this.hasSubscribed = interaction["hasSubscribed"];
    this.wasLiked = interaction["wasLiked"];
    this.wasDisliked = interaction["wasDisliked"];
    this.hasSaved = interaction["hasSaved"];
  })
);
Avius
  • 5,504
  • 5
  • 20
  • 42
Anton Marinenko
  • 2,749
  • 1
  • 10
  • 16
  • The call to `subscribe` should be after `pipe`, i.e. `.pipe(switchMap(...)).subscribe()`. – Avius Oct 25 '20 at 11:16
1

To transform Observables you need to use piping i.e call the pipe() method on the observable and pass in the an rxjs transformation operator

Example we can transform your code to


this.sharedSrv.postDetail.pipe(
  switchMap(post => {
    if(post) {
      this.hasPost = true;
      this.post = 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;
         return this.postsSrv.getPostInteraction(this.user.id, this.post.id, this.post.user.id)
      } else {
        this.isLoggedIn = false;
        return of (null) // You need import { of } from 'rxjs'
      }
    }
  )), 
  tap( interaction => {
    if(interaction) {
      this.hasSubscribed = interaction["hasSubscribed"];
      this.wasLiked = interaction["wasLiked"];
      this.wasDisliked = interaction["wasDisliked"];
      this.hasSaved = interaction["hasSaved"];
    }
  })
  
).subscribe()
Owen Kelvin
  • 14,054
  • 10
  • 41
  • 74