1
\$\begingroup\$

I have two APIs which fetch data of teachers and students from a given date range. I am using forkJoin to get both APIresponse and display the data with a regular interval of 10 ms.

This is working fine BUT when a user changes the date range from the date drop-down, then the previous subscription should be canceled and a new subscription will start with a given date range.

Also, I need to unsubscribe this subscription when the page changes and subscribe again when coming back to this page.

import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { TimerObservable } from 'rxjs/observable/TimerObservable';
import { Subject } from 'rxjs';

@Component({
    selector: 'app-school-data',
    templateUrl: './school-data.component.html',
    encapsulation: ViewEncapsulation.None
})

export class SchoolDataComponent implements OnInit {

    selectedFrequency: Frequency;
    studentList: Student[] = [];
    teacherList: Teacher[] = [];
    private livedata$ = new Subscription();
    componentDestroyed$: Subject<boolean> = new Subject();

    // approach 1
    private alive: boolean;
    private interval: number;

    constructor() {
        this.alive = true;
        this.interval = 10000;
    }

    ngOnInit() {
        this.getSchoolData();
        this.frequencyList = this.frequencySelector(FrequencyType);
    }

    private frequencySelector(definition) {
        return Object.keys(definition).map(key => ({ value: definition[key], name: key }));
    }

    private getDefaultData() {
        this.service.getSchoolData().subscribe(
            (data) => {
                this.selectedFrequency = this.frequencyList.filter((fl) => fl.name === data.frequency.value)[0];
                this.teacherList = data.teachers;
                this.studentlist = data.students;
                this.getLiveData(this.selectedFrequency);
            }
            , (err) => {
                throw err;
            }
        );
    }


    private getLiveData(selectedFrequency) {
        let live$ = Observable.forkJoin(
            this.service.getTecherData(selectedFrequency)
                .catch(error => {
                    return Observable.throw(error);
                }),
            this.service.getStudentData(selectedFrequency)
                .catch(error => {
                    return Observable.throw(error);
                })
        );

        this.livedata$ = TimerObservable.create(0, this.interval)
            // .takeWhile((s) => this.alive) // first approach
            .switchMap(() => live$)
            // .takeUntil(this.componentDestroyed$) // second approach
            .subscribe(
                ([teachers, students]) => {
                    console.log(`<==== livedata ${selectedFrequency.value}==> `);
                    console.log('teachers', teachers);
                    console.log('students', students);
                    this.teacherList = data.teachers;
                    this.studentlist = data.students;
                }
                , (err) => {
                    console.warn('errr', err);
                }
            )
        console.groupEnd();
        this.livedata$;
    }


    // called on change of drop down
    frequencyChanged(newValue) {
        console.log('frequencyChanged called', newValue, this.selectedFrequency);
        if (newValue !== this.selectedFrequency) {
            this.selectedFrequency = newValue;
            if (this.livedata$) {
                console.log('unsubscribing previous observable', this.livedata$);
                this.livedata$.unsubscribe();
            }
        }
        this.getLiveData(this.selectedFrequency);
    }

    ngOnDestroy() {
        this.alive = false; // first apparoch
        // second approach
        this.componentDestroyed$.next(true);
        this.componentDestroyed$.complete();
    }

}

I have done with this but there is confusion whether to use approach 1 or approach 2.

This is main code using forkJoin as below which combine the of both API observable.

let live$ = Observable.forkJoin(
        this.service.getTecherData(selectedFrequency)
            .catch(error => {
                return Observable.throw(error);
            }),
        this.service.getStudentData(selectedFrequency)
            .catch(error => {
                return Observable.throw(error);
            })
);

First approach: using the takeWhile() operator

    constructor() {
        this.alive = true;
        this.interval = 1000;
    }
    this.livedata$ = TimerObservable.create(0, this.interval)
        .takeWhile((s) => this.alive) 
        .switchMap(() => live$)
        .subscribe( ([teachers, students]) => { ... }

        ngOndestroy() {
            this.alive = false;
        }

Second approach: using the takeUntil() operator

componentDestroyed$: Subject<boolean> = new Subject();


this.livedata$ = TimerObservable.create(0, this.interval)
            .switchMap(() => live$)
            .takeUntil(this.componentDestroyed$)
            .subscribe(
                ([teachers, students]) => { ... }
            )

ngOnDestroy() {
        this.componentDestroyed$.next(true);
        this.componentDestroyed$.complete();
    }

Which is a better way to accomplish this?

Also, is switchMap() required here? If yes, then does this come before or after takeUntil() / takeWhile()?

There are so many operators in observables and it makes me confused. For example, do I need to to use startWith or combineLatest()?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.