Home > Software design >  RXJS: Multiple Subscriptions in Order
RXJS: Multiple Subscriptions in Order

Time:06-16

I've searched the web (StackOverflow, Rxjs, blogs, etc.) for an answer to my specific case, but I've not found anything for what I'm specifically looking for.

In my Angular component, I have the following:

  ngOnInit(): void {
    this.activatedRoute.queryParams.subscribe((params: any) => {
      if (!!params.page) {
        if (params.page <= 1) this.skip = 0;
        else this.skip = (params.page - 1) * this.limit;
      }
    });
    this.activatedRoute.params.subscribe((params: any) => {
      this.filter = params.filter;
      this.term = params.term;
      this.year = params.year;
      this.month = params.month;
    });
    this.activatedRoute.data.subscribe((data: any) => {
      this.setProvider(data.route);
      this.loadData(data.route);
    });
  }

My problem is I need the queryParams and the params subscriptions to complete and assign the public values before the data subscription takes place (queryParams and params can complete in any order as they don't have dependencies on each other).

In all of the examples I've seen, they show where each subscription is dependent on the previous and uses the values of the previous (passed down as variables). If I was using pure functions, I could possibly see where this is beneficial, but given that the code-behind in Angular also functions as a ViewModel, I'm simply setting public variables and then referencing them in the setProvider and loadData functions.

The below code works (basically chaining pipes), but given my limited knowledge on all that there is Rxjs, I wasn't sure if there was a cleaner, more efficient way to write this. It seems rather silly to return an observable just to use its values in the next chain of the pipe.

  ngOnInit(): void {
      this.activatedRoute.queryParams.pipe(concatMap((query: any) => {
        if (!!query.page) {
          if (query.page <= 1) this.skip = 0;
          else this.skip = (query.page - 1) * this.limit;
        }

        return this.activatedRoute.params;
      })).pipe(concatMap((params: any) => {
        this.filter = params.filter;
        this.term = params.term;
        this.year = params.year;
        this.month = params.month;

        return this.activatedRoute.data;
      })).subscribe((data: any) => {
        this.setProvider(data.route);
        this.loadData(data.route);
      });
  }

Thanks everyone!

Edit

The below also works using concat:

  ngOnInit(): void {
    concat(
      this.activatedRoute.queryParams.pipe((q: any) : any => {
        let query = q.getValue();

        if (!!query.page) {
          if (query.page <= 1) this.skip = 0;
          else this.skip = (query.page - 1) * this.limit;
        }
      }),
      this.activatedRoute.params.pipe((p: any) : any => {
        let params = p.getValue();

        this.filter = params.filter;
        this.term = params.term;
        this.year = params.year;
        this.month = params.month;
      }),
      this.activatedRoute.data.pipe((d: any) : any => {
        let data = d.getValue();

        this.setProvider(data.route);
        this.loadData(data.route);
      })
    );
  }

CodePudding user response:

Thanks to Joosep for the answer. I've updated it due to the his recommended function signature being deprecated.

Here's the updated version:

  ngOnInit(): void {
    combineLatest([
      this.activatedRoute.queryParams,
      this.activatedRoute.params,
      this.activatedRoute.data,
    ]).subscribe(
      ([queryParams, params, data]) => {

        if (!!queryParams['page'] && queryParams['page'] <= 1) {
          this.skip = 0;
        } else {
          this.skip = (queryParams['page'] - 1) * this.limit;
        }

        if (params) {
          this.filter = params['filter'];
          this.term = params['term'];
          this.year = params['year'];
          this.month = params['month'];
        }

        if (queryParams && params && data) {
          this.setProvider(data['route']);
          this.loadData(data['route']);
        }
      }
    );
  }

CodePudding user response:

If it has to be done with manual subscriptions I would combine latest for the activatedRoute queryParams, params and data and with a basic if check run the last part when appropriate.

Something like this maybe:

    combineLatest(
      this.activatedRoute.queryParams,
      this.activatedRoute.params,
      this.activatedRoute.data,
      (queryParams: Params, params: Params, data: any) => ({
        queryParams
        params,
        data,
      })
    ).subscribe((res: {queryParams: Params; params: Params; data: any }) => {
     
      if (!!queryParams.page && queryParams.page <= 1) {
       this.skip = 0;
      } else {
        this.skip = (queryParams.page - 1) * this.limit;
      }

     if (params) {
      this.filter = params.filter;
      this.term = params.term;
      this.year = params.year;
      this.month = params.month;
     }

      if (queryParams && params && data) {
        this.setProvider(data.route);
        this.loadData(data.route);
      }
    });

Also try to enforce strong types if possible.

  • Related