Home > Back-end >  Unsubscribe observable in the same component but with dynamic parameters
Unsubscribe observable in the same component but with dynamic parameters

Time:04-22

FILTER SERVICE - observable for basic data filtering

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { Filter } from '../../models/filter.model';
import { Convert } from '../../utils/converter';

@Injectable({
  providedIn: 'root'
})
export class FilterService {

  private currentFilter$ = new BehaviorSubject<Filter>({
    page: 0,
    limit: 20
  });

  constructor() { }

  private init(name: string) {
    let filterStr = localStorage.getItem(name);
    if (filterStr) {
      this.currentFilter$.next(Convert.fromJson<Filter>(filterStr))
    } else {
      localStorage.setItem(name, Convert.ToJson<Filter>(this.currentFilter$.value));
    }
  }

  private saveFilter(name: string, filter: Filter) {
    this.currentFilter$.next(filter);
    localStorage.setItem(name, Convert.ToJson<Filter>(filter));
  }

  public getFilter(name: string) : Observable<Filter> {
    this.init(name);
    return this.currentFilter$;
  }

  public nextPage(name: string) {
    let filter = this.currentFilter$.value;
    filter.page = filter.page   1;
    this.saveFilter(name, filter);
  }

  public pageLimit(name: string, limit: number) {
    let filter = this.currentFilter$.value;
    filter.limit  = limit;
    this.saveFilter(name, filter);
  }

  public scroll() {
    let filter = this.currentFilter$.value;
    filter.limit  = 20;
    this.currentFilter$.next(filter);
  }

  public resetPage(name: string) {
    let filter = this.currentFilter$.value;
    filter.page = 0;
    filter.limit = 20;
    this.saveFilter(name, filter);
  }

  public search(name: string, search: string) {
    let filter = this.currentFilter$.value;
    filter.search = search;
    filter.page = 0;
    filter.limit = 20;
    this.saveFilter(name, filter);
  }

  public sort(name: string, column: string, direction: string) {
    let filter = this.currentFilter$.value;
    filter.limit = 20;
    filter.page = 0;
    if (direction != '') {
      filter.orderBy = column   ' '   direction;
    } else {
      filter.orderBy = undefined;
    }
    this.saveFilter(name, filter);
  }
}

DOCUMENT ROUTE - this component has dynamic parameter type: 302 = invoice, 306 = delivery note etc.

{ path: 'documents/:type', component: DocumentsComponent },

DOCUMENT COMPONENT

@Component({
  selector: 'app-documents',
  templateUrl: './documents.component.html',
  styleUrls: ['./documents.component.scss']
})
export class DocumentsComponent extends Unsubscriber implements OnInit {

  displayedColumns: string[] = ['number', 'customerName', 'date', 'paymentDate', 'settledStatus', 'net', 'vat', 'gross'];
  documentsDataSource: Document[] = [];
  sortColumn = '';
  sortDirection: SortDirection = '';
  searchText = '';
  title = '';
  filters?: FilterItem[] = [];
  type = 0;

  constructor(
    private documentsService: DocumentsService,
    private filterService: FilterService,
    private route: ActivatedRoute,
    private router: Router
  ) {
    super();
    this.route.params.subscribe((params) => {
      this.type = params["type"];
      this.title = this.getTitle(params["type"]);
     this.filterService.getFilter(`documents:${this.type}`).subscribe((filter) => {
        if (filter.orderBy) {
          this.sortColumn = filter.orderBy.split(" ")[0];
          this.sortDirection = filter.orderBy.split(" ")[1] === "asc" ? "asc" : "desc";
        }
        if (filter.search) {
          this.searchText = filter.search;
        }
        this.subscription.add(this.documentsService.getDocuments(filter, params["type"]).subscribe(result => {
          this.documentsDataSource = result;
        }));
      });
    });
  }

UNSUBSCRIBER

import { Injectable, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';

@Injectable()
export abstract class Unsubscriber implements OnDestroy {

  subscription = new Subscription();

  constructor() { }

  ngOnDestroy(): void {
    console.log("unsubscribe");
    if (this.subscription) {
      this.subscription.unsubscribe();
    }
  }
}

RESULT - With each change of the route (parameter) in the document context I am getting 1 subscription to filters and documents, because the Unsubscriber is never called. If I change route between different components, like Products or Customers and than go back to Documents everything is fine.

How should I fix this?

subscription result

CodePudding user response:

I am seeing some anti-patterns here which might be a part of your issue.

Your are creating Subscriptions inside other .subscribe() callbacks. This should always be avoided by chaining other RxJS operators like switchMap together.

this.subscription.add(this.route.params.pipe(
  switchMap((params) => {
    this.type = params["type"];
    this.title = this.getTitle(params["type"])

    return combineLatest([this.filterService.getFilter(`documents:${this.type}`), of(params])
  }),
  switchMap(([filter, params]) => {
    if (filter.orderBy) {
      this.sortColumn = filter.orderBy.split(" ")[0];
      this.sortDirection = filter.orderBy.split(" ")[1] === "asc" ? "asc" : "desc";
    }
    if (filter.search) {
      this.searchText = filter.search;
    }

    return this.documentsService.getDocuments(filter, params["type"])
  })
).subscribe(result => {
  this.documentsDataSource = result
}))

Now you only have 1 Subscription which can be added to this.subscription.

  • Related