8
votes

The project, I am working on, has a lot of lists with search, sort and pagination. I am perfectly able to fetch data from API using these criteria (search, sort, pagination).

However, I was asked to make a "shareable links" so users can share their queried lists in between. If user A has his list ordered by 'Customer' and on page 5, sends link to user B, he or she will open the same list on page 5 ordered by 'Customer'. Simple.

I have a working solution. I subscribe to queryParams on ActiveRoute, parse these params and then reload list. See the code below.

component template html:

<ngb-pagination [collectionSize]="size" [page]="page" (pageChange)="changePage" >
</ngb-pagination>
<pre>Current page: {{page}}</pre>

component typescript:

ngOnInit() {

    this.route
      .queryParams
      .subscribe(queryParams => {

          this.sort = queryParams['sort'];
          this.search = queryParams['search'];
          this.page= +queryParams['page'] || 1;

          this.refresh();
    });

    this.refresh();
}

refresh() : void {

    this.transactionsService.fetch({
      from: (this.page - 1) * this.size,
      take: this.size,
      search: this.search,
      sort: this.sort
    }).subscribe(data => {

      this.entities = data.entities;

      this.total_size = data.total;
    });

}

changePage(event: number) : void {

    this.router.navigate(['/transactions'], { 
      queryParams : { page: event},
      queryParamsHandling: 'merge'
    });
  }

However, I consider this a dirty solution, particularly any action is handled through router. I would like first to avoid subscribe in ngOnInit and secondly update changePage function this way:

 changePage(event: number) : void {

    this.page = event;

    this.refresh();

    // update query params without triggering navigation
    this.route.queryParams['page'] = this.page; ??????
  }'

How is this possible?

1
Why do you wish to avoid subscribing in ngOnInit ? That is the correct way to handle changes in query parameters, and navigating to the same page with changed query parameters will then fire that subscription and work correctly - I would say your solution is correct and clean, and your proposed change would make it less correct and much messier.Mark Hughes
Well, I do not have any benchmark yet, so I can not say which solution is faster or in other words, how use of router negatively affects performance. However, I haven't found any blog, tutorial or even official samples that works the same way as in my current solution. That is the source of my concerns.Luke1988
I have a first drawback of my current solution: user types 'bills' to search input and hit search button. Angular navigates to route /transaction?search=bills. Subscribe to queryParams in ngOnInit() handles change properly and data are fetched from API. So far so good. However, if user wants to search again with same query, there is actually no change in queryParams so subscribe has nothing to handle.Luke1988

1 Answers

16
votes

Instead of navigation you can use location provider

import { ActivatedRoute, Router } from '@angular/router';  
import { Location } from '@angular/common';

...
constructor(
    private router: Router,
    private location: Location,
    private activatedRoute: ActivatedRoute,
    ...
)

changePage(event: number) : void {
  ...       
  this.location.go(`${this.activatedRoute.url}?page=${event}` );
  ...
}

this.location.go will not refresh window in this case.

The way of dynamic creating URL:

const urlTree = this.router.createUrlTree([], {
    queryParams: { page: event },
    queryParamsHandling: 'merge',
    preserveFragment: true 
});

this.location.go(urlTree)

But you can leave subscription inside ngOnInit hook, it's ok. To prevent router to update browser history you have to pass replaceUrl: true parameter

this.router.navigate([], { 
  relativeTo: this.activatedRoute, 
  queryParams: 
  {
      page: event
  },
  replaceUrl: true,
});

without forcing onSameUrlNavigation: 'reload' configuration angular router will not refresh your page anyway, it's ok to leave your solution as is