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
- https://angular-checklist.io/default/checklist/rxjs/Z1eFwa9
- https://www.thinktecture.com/en/angular/rxjs-antipattern-1-nested-subs/
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