4

I have a path declared like:

{
  path: 'app/:userId',
  children: [...AppChildrenRoutes]
}

And then, inside AppChildrenRoutes

{ path: 'feature', component: MainFeatureComponent }

So, at some point of my application I can have localhost:4200/app/123/feature. Inside this component, with some action, I can navigate to another route like this:

this.router.navigate([
  'app',
  this.userId,
  'feature',
  { search: this.searchFor }
]);


Consider it as a big enterprise sized application that is changing architecture, to use NgRx, step by step.
So, I faced a problem with Router Store. I've set up everything and it works.
Following Router Store docs I wrote a Custom Serializer and it looks like this

serialize(routerState: RouterStateSnapshot): RouterStateUrl {
  const { url } = routerState;
  const queryParams = routerState.root.queryParams;

  while (route.firstChild) {
    route = route.firstChild;
  }
  const params = route.params;

  return { url, queryParams, params };
}

And I found out that, considering a URI like

localhost:4200/app/123/feature;search=blue

the route.params returns only the search param, not both userId and search.

- How can I implement a CustomSerializer that returns all params from the path? (in this case, both userId and search).

I kinda tried but failed to, each iteration of the while loop, check if there was a param and add them to a object until the last one. Good, Ok or Bad approach? How could I work that and not fail?
Thanks.

João Ghignatti
  • 2,281
  • 1
  • 13
  • 25

3 Answers3

5

I had a similar problem and solved it by extending the params object every time I go through the loop.

My CustomSerializer now looks like this:

export class CustomSerializer implements RouterStateSerializer<RouterStateUrl> {
serialize(routerState: RouterStateSnapshot): RouterStateUrl {
    let route = routerState.root;

    let params = {};
    while (route.firstChild) {
        params = {
            ...params,
            ...route.params
        };
        route = route.firstChild;
    }

    const {
        url,
        root: { queryParams }
    } = routerState;

    return { url, params, queryParams };
}

}

coettl
  • 66
  • 2
0

You can select any of the parent/child route using a selector and you don't need serializer for this. You have an object of root Route and by creating a selector you can iterate through the routes and get the required parameter.

const selectRouter = createFeatureSelector<XStore,
    fromRouter.RouterReducerState<any>>('router');

export const selectAllRouteParam = (param: string) => createSelector(
    selectRouter,
    (router: RouterReducerState) => {
        let current = router.state.root;

        while (current) {
            if (current.params && current.params[param]) {
                return current.params[param];
            } else {
                current = current.firstChild;
            }
        }
    }
);

While using you can use it like this:

userId$ = this.store.select(selectAllRouteParam('userId'))

Late to the party, but I hope someone in the future will find this helpful.

SureshS
  • 589
  • 8
  • 23
0

Actually slightly modified version (not selected as proper anwser) works for me in Angular v16.

export class CustomSerializer implements RouterStateSerializer<RouterStateUrl> {
serialize(routerState: RouterStateSnapshot): RouterStateUrl {
    let route = routerState.root;

    let params = {};
    while (true) {
      params = {
        ...params,
        ...route.params,
      };

      route = route.firstChild;
      if (!route) {
        break;
      }
    }

    const {
        url,
        root: { queryParams }
    } = routerState;

    return { url, params, queryParams };
}
Dariusz Filipiak
  • 2,858
  • 5
  • 28
  • 39