angulartypescriptngrxangular-routingngrx-router-store

How to set default query params for Route in Angular 7?


In our Angular-7-Application, we use @ngrx and @ngrx/router-store to get the query params into the state.

A few components of the application are paginated lists. We have every list as a component and the Pagination-Component included in every list.

The current page is stored in the URL as a query Parameter: user/:userId/agent?page=0 and the PaginationComponent gets the current page from state.router.state.queryParams.page. However, if a user accesses the URL user/:userId/agent, queryParams.page returns undefined.

We could solve this by using state.router.state.queryParams.page || 0 in every component but I wonder, if there is an easier way - can a Route without query params be redirect to a Route with query params?

I tried using the most obvious redirect:

{ path: 'user/:userId/agent', redirectTo: '/user/:userId/agent?page=0', pathMatch: 'full' },
{ path: 'user/:userId/agent?page=0', component: AgentListComponent },

but I get Error: Cannot match any routes. URL Segment: 'user/max/agent'.

The only feature request I found was this one where the error above appears.


Solution

  • For your question specifically:

    can a Route without query params be redirect to a Route with query params?

    I don't think this can work because the ? in a query is a separator, which is not part of a URL's query string.

    Alternative 1 - since you are using ngrx, one way to do it is to use the custom serializer. The docs from the ngrx.io site show an example of returning the params with serialization. This is where you can add logic to add a default to the params if it doesn't exist. I'll disclaim this may be less ideal because it'll fire on each route, but it can make your routes simpler.

    import { Params, RouterStateSnapshot } from '@angular/router';
    import { RouterStateSerializer } from '@ngrx/router-store';
    
    export interface RouterStateUrl {
      url: string;
      params: Params;
      queryParams: Params;
    }
    
    export class CustomSerializer implements RouterStateSerializer<RouterStateUrl> {
      serialize(routerState: RouterStateSnapshot): RouterStateUrl {
        let route = routerState.root;
    
        while (route.firstChild) {
          route = route.firstChild;
        }
    
        const {
          url,
          root: { queryParams },
        } = routerState;
        const { params } = route;
    
        // Add here
        if (<insert url logic> && queryParams.page === undefined) {
            queryParams.page = 0;
        }
    
        // Only return an object including the URL, params and query params
        // instead of the entire snapshot
        return { url, params, queryParams };
      }
    }
    

    Alternative 2 - You can wrap the HttpClient or more preferably, create a generic page list method that checks for this and adds it to the request if there is no page. This answer shows an example of how to achieve adding params.

    Alternative 3 - You can use the page as a part of the path and do work arounds/changes as needed to generate your requests.

    { path: 'user/:userId/agent', redirectTo: '/user/:userId/agent/0', pathMatch: 'full' },
    { path: 'user/:userId/agent/:page', component: AgentListComponent },