0

I have a dynamic BreadCrumb navigation where I need to get the params from URL. To achieve this, I'am executing the following code:

const userId = this.activatedRoute.params
               .pipe(skipWhile((params: Params) => Number.isNaN(+params['userId'])));

const getAppUserId = userId.pipe(
            switchMap((params: Params) => {
                return +params['userId']
                    ? this.myAppService.getApplicationUser(+params['userId'])
                    : of('')
            }))

But it's always empty and I never get the params passed to the Observable. When I use queryParams, it works fine, but I don't want to go for queryParams concept. Any idea where I'am going wrong?

k.vincent
  • 3,743
  • 8
  • 37
  • 74
  • Please provide a [Stacklitz](https://stackblitz.com/) or any other [minimal-reproducible-example](https://stackoverflow.com/help/minimal-reproducible-example) to reproduce your error. – zerocewl Oct 02 '19 at 12:17
  • @zerocewl: I'am trying to provide a Stackblitz Demo. Need a bit time. – k.vincent Oct 02 '19 at 12:19

3 Answers3

0

I once made an automated breadcrumb builder that only relies on a static data in your routes. I managed to find it back, so here you go, in case it helps :

export class RouteBreadcrumbsComponent {

  breadcrumbs = [];

  constructor(
    private router: Router,
  ) {
    let buffer = [];

    /* 
       Listen for router events in reverse order (to get all events).
       From the snapshots, return its path and its breadcrumb.
    */
    this.router.events.pipe(
      filter(event => event instanceof ActivationEnd),
      map((event: ActivationEnd) => [
        event.snapshot.data && event.snapshot.data.crumb || undefined,
        this.determineCorrectPath(event.snapshot),
      ]),
      filter(([crumb, url]) => !!crumb),
    ).subscribe(([crumb, url]) => buffer.push({ crumb, url }));

    this.router.events.pipe(
      filter(event => event instanceof NavigationEnd)
    ).subscribe(event => {
      this.breadcrumbs = buffer.reverse();
      buffer = [];
    });
  }

  determineCorrectPath(snapshot: ActivatedRouteSnapshot) {
    const path = snapshot.routeConfig.path;
    const params = snapshot.params;

    // Path = route with a param, so get the param
    if (path.startsWith(':'))
      return params[path.replace(':', '')];
    // Path = '' = route group or root route of a lazy loaded module, so take the parent path
    else if (!path)
      return snapshot.parent.routeConfig.path;
    // Path can be used as is
    else
      return path;
  }

  buildRoutingArray(crumbIndex: number) {
    return this.breadcrumbs
      .slice(0, crumbIndex + 1)
      .map(crumb => crumb.url)
      .join('/');
  }
}

All you have to do is provide a data: { crumb: 'Your component crumb' } to each of your routes and it should work.

  • Not working as expected. Dosen't show nothing. Didn't you miss any thing? Should `buildRoutingArray(crumbIndex: number)` not be invoked? As it looks now, it's not invoked at all. – k.vincent Oct 02 '19 at 10:33
  • @k.vincent [it works](https://stackblitz.com/edit/angular-eu8uuh?file=src%2Fapp%2Froute-breadcrumbs%2Froute-breadcrumbs.component.html). But I'm not giving it so that you can use it, I'm giving it to help you understand how it works. So no, I didn't miss anything, it works as epxected, and `buildRoutingArray` is a HTML function that displays the crumbs. –  Oct 02 '19 at 10:42
  • Ok. I get it. Great. I'll follow the approach. – k.vincent Oct 02 '19 at 10:44
  • I see. Good one. But my issue is to get param `id` from/via observable and after that use it for: `getAppUserId` with switchMap to make a HTTP call with the param `id` which I got from `snapshot.params`. This where I'am stuck. – k.vincent Oct 02 '19 at 11:13
  • @k.vincent making another answer for that, wait a minute –  Oct 02 '19 at 11:15
0

First issue :

Number.isNaN(+params['userId'])

Returns a string, not a number.

Second issue :

const getAppUserId = userId.pipe(
  switchMap((params: Params) => {

You're not getting the params from the observable, but the user ID (well, a boolean actually).

If your wish is to "get param id from/via observable and after that use it for: getAppUserId with switchMap to make a HTTP call with the param id which I got from snapshot.params"

Then the correct code would be

this.activatedRoute.params.pipe(
  map(params => params.id),
  switchMap(userId => this.myAppService.getApplicationUser(userId)
);

You should not test the validity of the ID : URL are strings, you can't trust a user. Simply make the request, and if the ID doesn't match anything, then your API won't return anything. Let the API make all the controls on the ID, not your client.

  • Still no `params.id`. I changed `userId` to be: `this.activatedRoute.params.pipe(skipWhile((params: Params) => params['id']))` I think the issue is still in this part where I'am using `skipWhile`. Do you mind if can continue in chatRoom instead of here? – k.vincent Oct 02 '19 at 11:52
  • @k.vincent you can, but I'm going to answer even less (I have stack notifications on my phone but not for the chat room). Best would be to make a [mcve] of your issue so that I can see it first hand and correct it. –  Oct 02 '19 at 11:55
  • Here is a demo https://stackblitz.com/edit/angular-m36dmr, but it's not working, as I tried to get all the necessary files but the concept is a kind of large and complicated that I can provide all files. But the demo would give you enough idea what I'am really trying to rech. – k.vincent Oct 02 '19 at 14:37
0

Issue resolved. Was kind of simple...the fix was to catch the param from route.snapshot.

this.router.events
        .pipe(filter(event => event instanceof NavigationEnd))
        .pipe(map(() => {return this.activatedRoute;}))
        .pipe(
            map((route) => {
                while (route.firstChild) {route = route.firstChild;}
                return route;
            })
        ).subscribe(route => {
             console.log(`param: ${route.snapshot.params['id']}`);
             ...
             ...
        });
k.vincent
  • 3,743
  • 8
  • 37
  • 74