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.

3
  • 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. Commented Oct 25, 2020 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. Commented Oct 25, 2020 at 11:48
  • Fair enough and no problem (no need to apologise), just something to keep in mind in the future :] Commented Oct 25, 2020 at 17:11

3 Answers 3

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 => {
    ...
  }
);
Sign up to request clarification or add additional context in comments.

Comments

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()

Comments

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"];
  })
);

1 Comment

The call to subscribe should be after pipe, i.e. .pipe(switchMap(...)).subscribe().

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.