5

My AppComponent has a @RouteConfig decorator that defines the top-level routes:

@RouteConfig([
  {
    path: '/',
    name: 'Home',
    component: HomeComponent
  },
  {
    path: '/about',
    name: 'About',
    component: AboutComponent
  },
  {
    path: '/profile/...',
    name: 'Profile',
    component: ProfileComponent
  }
])

export class AppComponent {
}

My ProfileComponent has a @RouteConfig decorator that defines the Profile child routes:

@RouteConfig([
  {path: '/', component: ProfileDetailsComponent, name: 'View', useAsDefault: true},
  {path: '/:id', component: ProfileDetailsComponent, name: 'Public'},
  {path: '/edit', component: ProfileEditorComponent, name: 'Edit'},
])

export class ProfileComponent {
}

When i'm inside ProfileDetailsComponent I can do redirects to other Profile routes but not others. I want to avoid specifying the url using navigateByUrl and use route names instead, using navigate. E.g.:

this.router.navigate(['View']); // Works
this.router.navigate(['About']); // Raises error that it does not exist

I read this answer here: Angular 2 - How to navigate to another route using this.router.parent.navigate('/about')

It uses:

this.router.parent.navigate(['About']);

Which is sort of ok, but solves my problem only when I know at declaration time where the redirect should go. I have multiple levels of nesting and determine the target route at runtime. I'm looking for a way to do something like:

this.router.navigate(['Level1', 'Level2', 'Level3']);

Which allows me to track somewhere the fully qualified name of the target route. Is this possible in any way?

Community
  • 1
  • 1
fips
  • 4,319
  • 5
  • 26
  • 42

2 Answers2

6

Prefix the route name with a slash to indicate that the route is a root route

this.router.navigate(['/About']); 
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Do you have any blog posts that you could share that you have authored, I see you all the time on here answering everyone's **Angular2** questions? – David Pine May 16 '16 at 22:26
  • Yep worked! But in my scenario I need the flexibility/abstraction the other solution provides. Thank you! – fips May 17 '16 at 15:07
4

This is where a microservice comes in handy, check out the service docs here. The idea is that the parent can listen for child requests to navigate and the child can broadcast said changes as desired.

Let's start with a simple object that will store the route name and the optional arguments:

export class NavArgs {
   constructor(routeName: string, args?: any) { }
}

Now let's define the microservice, with rxjs this is really easy:

import { Injectable } from '@angular/core'
import { Subject }    from 'rxjs/Subject';

@Injectable()
export class NavigationService {
  private navigationRequestedSource = new Subject<string>();

  onNavigationRequested$ = this.navigationRequestedSource.asObservable();

  requestNaivation(navArg: string) {
    this.navigationRequestedSource.next(mission)
  }
}

Now a child component can consume it and request a navigation:

export class SomeChild {
   constructor(navService: NavigationService) { }
   invoke() {
      this.navService.requestNaivation(new NavArgs('SomeRoute'));
   }
}

And a parent component can listen for said request and act on it:

export class SomeParent {
   constructor(navService: NavigationService, router: Router) {
      this.navService
          .onNavigationRequested$
          .subscribe((navArgs: NavArgs) => {
              if (navArgs) {
                  if (navArgs.args) {
                      this.router.navigate([ navArgs.routeName, navArgs.args ]);
                  }
                  else {
                      this.router.navigate([ navArgs.routeName ]);
                  }
              }
      });
   }       
}

Obviously this is the very basic code example, there would need to be more imports and error handling and such. But hopefully you get the idea.

Note

It is important to ensure that both the parent and child use the same instance of this service, in order to do that - you must set it as a provider at the highest level where it is being used. That is because by default Angular2 creates a singleton for DI and uses a hierarchical approach. You must ensure that you do not mistakenly set it as a provider at a lower level, otherwise it will not work.

David Pine
  • 23,787
  • 10
  • 79
  • 107
  • Like the idea, seems neat and generic as I wanted. Will try it after work and come back with feedback! – fips May 16 '16 at 17:31
  • 1
    It's probably a good time to upgrade to the new router as well, if you're using the Angular 2 release candidate, as things like route names are now deprecated. – Dan May 16 '16 at 18:21
  • Awesome this worked great at any combination levels of parent/child components. Thank you! – fips May 17 '16 at 15:03
  • I learned angular2 last week for a hackathon so sorry if this sounds silly but what do you mean `new router`? Is it using `Routes` instead of `RouteConfig` cos i've seen that in a couple of blogs.. – fips May 17 '16 at 15:05
  • 1
    @fips, this is referring specifically to **Angular2 RC***, with the scoped approach rather than the bundled. – David Pine May 17 '16 at 15:37