4

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.

4

3 回答 3

3

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 },
于 2019-01-19T15:47:03.080 回答
1

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

To have query params and state synchronized you need an effect that captures any action that results in a page change in your application. Inside the event, you'll have something like:

@Effect({dispatch:false})
setRouteParams = this.actions$.pipe(
    ofType<ActionCausingPageChange>("action name"),
    tap( action =>{

        let a = { page: action.payload.page };
        // or in case it's not part of action payload, get it from store
        this.router.navigate(
            [], {
                relativeTo: this.route,
                queryParamsHandling: 'merge',
                queryParams: a
            });
        }
    )
);

Then have a meta reducer to update state from query params on page reload:

export function initStateFromQueryParams(
    reducer: ActionReducer<AppState>
): ActionReducer<AppState> {
    return function(state, action) {
        const newState = reducer(state, action);
        if ([INIT.toString(), UPDATE.toString()].includes(action.type)) {
            const urlParams = new URLSearchParams(window.location.search);
            return { ...newState, page: urlParams.get("page") };
        }
        return newState;
    };
}

This way you always know if page number changes it's gonna be reflected in url consequently. So even if you go to a new route(component) after that route gets its initial data the effect will trigger which updates query params.

You may want to checkout out this phenomenal article about state management in angular apps

于 2019-05-17T16:51:04.403 回答
0

For me this worked on the root path:

{
  path: '',
  redirectTo: '/foo?bar=baz',
  pathMatch: 'full'
}

However when trying the same with a named param (like your :userId) it doesn't work

于 2019-08-15T07:57:54.913 回答