3

Given the following module, how can I create the routes so that when the application loads this module it will route to CComponent and have the AComponent loaded in the named router outlet search-results

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';

import { AComponent } from './a.component';
import { BComponent } from './b.component';
import { CComponent } from './c.component';

@NgModule({
  declarations: [
    AComponent,
    BComponent,
    CComponent
  ],
  imports: [
    BrowserModule,
    RouterModule.forRoot([
      {
        path: '',
        pathMatch: 'prefix',
        component: CComponent,
        children: [
          {
            path: 'a',
            component: AComponent,
            outlet: 'search-results'
          },
          {
            path: 'b',
            component: BComponent,
            outlet: 'search-results'
          }
       ]
    ])
  ],
  providers: [],
  bootstrap: [CComponent]
})
export class AppModule {}

a.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'a-component',
  template: `
      <div class="tabs row">
        <h3 [routerLink]="[{ outlets: { 'search-results': ['a'] } }]" class="tab" routerLinkActive="active">
          A
        </h3>
        <h3 [routerLink]="[{ outlets: { 'search-results': ['b'] } }]" class="tab" routerLinkActive="active">
          B
        </h3>
      </div>
    <router-outlet name="search-results"></router-outlet>
  `
})
export class AComponent {
}

I have tried a number of different things with the routes:

With the above configuration, the page loads, but the AComponent isn't loaded onto the screen.

If I update CComponent to have the following:

export class CComponent {
  constructor(
    private route: ActivatedRoute,
    private router: Router
  ) {
    router.navigate(
      [
        {
          outlets: {
            'search-results': ['a']
          }
        }
      ], { relativeTo: route });
  }
}

Then everything seems to work, but it seems wrong to have to trigger that navigation in the constructor of the parent element.

If I update the child routes and replace { path: 'a', component: AComponent, outlet: 'search-results' } with { path: '', component: AComponent, outlet: 'search-results' }, the router seems to load that component correctly in the outlet, but the routerLinkActive directive doesn't seem to take effect as the active class isn't added to the first h3 and updating the routerLink to outlets: { 'search-results': ['a'] } doesn't allow navigation back to the AComponent after navigating to the BComponent.

I've tried a number of variations on the above all with no success.

Is there a way to configure the routes so that the default route will load CComponent in the main unnamed router-outlet and AComponent in the named search-results router-outlet?

Plunkr

peinearydevelopment
  • 11,042
  • 5
  • 48
  • 76

1 Answers1

3

Currently, you are defining the child routes to your base route, one of which is /a (the path that loads AComponent into the search-results outlet), but you are not actually going to that path when the application loads. It works when you imperatively navigate to that route in your CComponent (which does get initialized on load).

If you want the application to load initially with the (search-results):a route active, you can use the redirectTo property in your route definition.

In your case, I would pattern match on the initial route (the empty path: '') and redirect to the path with /a loaded, like this:

RouterModule.forRoot([
      /* this line will match the initially loaded route and 
       immediately redirect to the child route 'a', which loads 
       `AComponent` into the `search-results` outlet */
      { path: '', pathMatch: 'full', redirectTo: '(search-results:a)'},

      {
        path: '',
        pathMatch: 'prefix',
        component: CComponent,
        children: [
          {
            path: 'a',
            component: AComponent,
            outlet: 'search-results'
          },
          {
            path: 'b',
            component: BComponent,
            outlet: 'search-results'
          }
       ]
      }
    ])

Note that order matters in the snippet above. It will match the empty path, redirect to the child route, and the load the component you want in the designated outlet (as you defined in your code).

vince
  • 7,808
  • 3
  • 34
  • 41
  • Thanks, but this gives me the error `Error: Cannot match any routes. URL Segment: 'a'` in the console and nothing loads. – peinearydevelopment Jan 16 '18 at 14:54
  • What is the route that shows up in the URL when you load the app with the `navigate` method in the `CComponent` constructor? – vince Jan 16 '18 at 15:02
  • `http://localhost:4200/c/(search-results:a)`. I've tried redirecting to `(search-results:a)` and a number of similar combinations, but without luck. – peinearydevelopment Jan 16 '18 at 15:03
  • The issue is just the '/a' in the redirect. I can't get the context for the exact route you need based on the code in your question, but you just need to change the value in 'redirectTo' property above to the route where the `AComponent` loads (aka the route that gets loaded when you use `navigate` in the CComponent. Try redirecting to '/c/(search-results:a)' – vince Jan 16 '18 at 15:04
  • I have and it doesn't work. I'm happy to make a jsFiddle or something of that sort, but can't find out where/how to make one. – peinearydevelopment Jan 16 '18 at 15:05
  • What value do you get in the URL when you do `redirectTo: '/c/(search-results:a)` -- your problem may be related to this issue: https://github.com/angular/angular/issues/12371 – vince Jan 16 '18 at 15:07
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/163261/discussion-between-vincecampanale-and-peinearydevelopment). – vince Jan 16 '18 at 15:12