Navigate to parent path from router outlet (NG5)

Viewed 2057

Problem

Trying to navigate from http://localhost:4200/#/place/(popup:tabs-modal) (an outlet) to http://localhost:4200/#/place/someId

Notice that the outlet is basically a child route to "place".

What I can do

I can close the outlet, and just end up on: http://localhost:4200/#/place: this.router.navigate(['place', { outlets: { popup: null } }]);

Oh then its easy right?

this.router.navigate(['place', 'someId', { outlets: { popup: null } }]);should then navigate me to http://localhost:4200/#/place/someId right?

Wrong. The router generates a non-sensical route for me: "/place/(someId//popup:tabs-modal)"

Am I doing something wrong? Or is it just buggy?

4 Answers

I gave up trying to make .navigate() work, and ended up using router.navigateByUrl('/place/someId') instead. A simple solution that eluded me.

The accepted answer is bad practice since you surely don't want to navigateByUrl whenever you want to close your popup or whatever. Best thing to do is something like this in your router:

{
        path: 'place/:someid',
        component: PlacePageComponent,
        resolve: {someid: SomeIDResolver},
        children: [
            {
                path: 'tabs-modal/:maybenextidhere',
                outlet: 'popup',
                component: TabsModalComponent,
                resolve: {maybenextidhere: MaybeNextIDResolver},
            },
        ]
    },

And then here comes the trick. In your function where you want to close the popup, you want to call something like this:

this.router.navigate(['./', {outlets: { popup: null}}], {relativeTo: this.route.firstChild.firstChild});

I've encountered the same issue. The thing is that I wanted to be able to use my component in any depth. So https://stackoverflow.com/a/50976238/1617590 solution led me to create a slightly different and more flexible approach.

Helper method looks through main ActivatedRoute and returns the ActivatedRoute needed for relativeTo property to properly navigate out of the secondary outlet.

private getRelativeTo(
  obj: ActivatedRoute,
  callback: (lastFirstChild: ActivatedRoute) => void
) {
  Object.keys(obj).forEach((key) => {
    if (key === 'outlet') {
      if (obj[key] === PRIMARY_OUTLET) {
        const fChild = obj.firstChild;
        const nextFChild = fChild ? fChild!.firstChild : undefined;
        if (fChild && nextFChild) {
          nextFChild.outlet === 'modal' // name of your secondary outlet
            ? callback(fChild) // found child component with named outlet
            : this.getRelativeTo(fChild, callback) // not found, go deeper
        } else {
          // No named child outlet found so just return back.
          callback(this.route);
        }
      } else {
        // First child is not the primary, so just return back.
        callback(this.route);
      }
    }
  });
}

And this is how I use it to close my modal:


...

constructor(
  private router: Router,
  private route: ActivatedRoute
) {}

closeModal() {
  this.getRelativeTo(this.route, (lastFirstChild) => {
    this.router.navigate(['./', { outlets: { modal: null } }], {
      replaceUrl: true,
      relativeTo: lastFirstChild,
    });
  });
}

This approach helped me to properly navigate out from child routes with named auxiliary outlets when used on the same level as the primary outlet as well as when used as a child component.

Hope that helps.

Related