Home > front end >  How to refactor nested subscription in angular
How to refactor nested subscription in angular

Time:07-25

I have an angular13 app with rxjs7.5.0

I need to get list of students based on class code. Here is how I addressed this requirement.

export class StudentsComponent implements OnInit {
students: Student[] = [];
private appNum: string;
private sectionCode: string;

constructor(
 private route: ActivatedRoute,
 private studentSvc: StudentService
) {}

ngOnInit(): void {
 this.route.queryParamMap.subscribe((params) => {
  let classCode = params.get('cc') ?? '';

  classCode = decodeURIComponent(classCode);
  if (classCode.match(ALPHABETS_REGEX)) {
    this.studentSvc
      .fetchStudents({
        sec: sectionCode,
      })
      .subscribe((response) => {
        if (response.status == HttpStatusCode.Ok) {
          this.students = response.data(obj);
        }
      });
  }

This is functioning as per app requirements. However, I see this would end up in callback & hell chain & is an anti-pattern

Based on the links

Which operator & how can I get rid of this anti-pattern ?

Thanks!

CodePudding user response:

What you can do is something along these lines (see inline comments for explanation)

// you start from the this.route.queryParamMap and you transform it
// using a chain of pipeable operators
this.route.queryParamMap.pipe(
  // in this case you need only one pipeable operator, switchMap.
  // switchMap is a 'higher order' operator, i.e. an operator which returns
  // another Observable (as part of the transformation performed within
  // the pipe) - other higher order operators are concatMap, mergeMap (akak flatMap, exaustMap)
  switchMap((params) => {
     let classCode = params.get('cc') ?? '';
     classCode = decodeURIComponent(classCode);
     if (classCode.match(ALPHABETS_REGEX)) {
        // in this case you return the Observable returned by fetchStudents
        return this.studentSvc
          .fetchStudents({
            sec: sectionCode,
          })
     }
     // in the case the code does not match ALPHABETS_REGEX you have to return
     // another Observable to respect the signature of switchMap.
     // This Observable could be, for instance, an Observable that returns
     // an empty array (or anything else, depending on your requirement)
     return of([])
  })
).subscribe(
// do what you need to do
)

You may find this article interesting.

CodePudding user response:

You can use switchMap (Read here) since you have one inner Observable. If you have more than one then you can use mergeMap (Read here).

import { switchMap } from 'rxjs/operators';

ngOnInit() {
  this.route.queryParamMap.pipe(switchMap((params)=>{
    let classCode = params.get('cc') ?? '';
    if (classCode.match(ALPHABETS_REGEX)) {
      return this.studentSvc.fetchStudents({sec: sectionCode}).pipe((tap((response)=>{
               if (response.status == HttpStatusCode.Ok) {
               this.students = response.data(obj);
            }
         }
         
     )))
    }
  })).subscribe()
}

You need also to:

  • account for error handling and
  • unsubscribe() to avoid memory leaks
  • Related