Search code examples
angularangular-ui-routerjhipster

Handling 'Routes' of Imported Modules on Angular


I'm having some difficulties in understanding/using the routes of an imported module. The problem occurs on routes that rely on secondary outlets or named outlets.

The Problem

Suppose I have two modules entity-a and entity-b, and that entity-a imports entity-b, using its content via the selector app-entity-b on entity-a.component.html.

The content of entity-b will show perfectly on entity-a, however, ** entity-b links that refer to named outlets became broken**.

Application Test

This test application on stackblitz illustrates the issue.

Routes are working within the module

  • Click on EntityB to open entity-b.component.
  • Click on Entity B details to open entity-b-detail.component.

Routes are broken once the module is imported

  • Click on EntityA to open entity-a.component.
  • entity-a.componenet is importing entity-b.module and showing entity-b.component that contains the link to entity-b-detail.component.
  • Click on Entity B details and get an error:
    • Error: Uncaught (in promise): Error: Cannot match any routes. URL Segment: 'entity-a/a'

Things to take into consideration

  • Modules entity-a and entity-b are being lazily loaded.
  • entity-a is importing component entity-b and using its selector to show its content.
  • If the [routerLink] makes reference to entity-b path before calling the outlets a redirect occurs to entity-b component, showing the detail. However, this isn't the expected behavior.

Relevant code parts

app.module

const appRoutes: Routes = [
  {
    path: '',
    component: MainComponent
  }
];

const childRoutes: Routes = [
  {
    path: 'entity-a',
    loadChildren: './entities/entity-a/entity-a.module#EntityAModule'
  },
  {
    path: 'entity-b',
    loadChildren: './entities/entity-b/entity-b.module#EntityBModule'
  },
];

const ROUTES = [...childRoutes, ...appRoutes];
@NgModule({
  imports:      [
    BrowserModule,
    FormsModule,
    RouterModule.forRoot(appRoutes),
    RouterModule.forChild(childRoutes)
    ],
  declarations: [ AppComponent, MainComponent, NavComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

entity-a.module

const routeA: Routes = [
  {
    path: 'a',
    component: EntityAComponent
  }
];

@NgModule({
  imports: [
    CommonModule,
    EntityBModule,
    RouterModule.forChild(routeA)
  ],
  declarations: [EntityAComponent],
  exports: [EntityAComponent]
})
export class EntityAModule { }

entity-a.component.html

<h3>Entity A</h3>
<p>
Entity A Content
</p>

<div>
  <em>Importing 'Entity B'</em>
  <app-entity-b></app-entity-b>
</div>

entity-b.module

const routes: Routes = [
  { 
    path: 'b',
    component: EntityBComponent,
    children: [
      {
        path: 'detail',
        component: EntityBDetailComponent,
        outlet: 'details',
      },
    ]
  },

];

@NgModule({
  imports: [
    RouterModule.forChild(routes),
    CommonModule
  ],
  declarations: [
    EntityBComponent,
    EntityBDetailComponent
  ],
  exports: [
    EntityBComponent,
    EntityBDetailComponent
  ]
})
export class EntityBModule { }

entity-b.component.html

<h3>Entity B</h3>
<ul>
<li><a [routerLink]="[ { outlets: { details: 'detail' } }]">
  Entity B details
</a></li>
</ul>
<router-outlet name="details"></router-outlet>

Solution

  • The routing for named outlets work in combination with other routes. When you try to navigate to entity-b-detail from entity-a, it goes to the route entity-a/a/(details:detail), and displays the result in named outlet. Since it doesn't find any match, it throws the error.

    I've forked your code and made changes here.

    The only relevant change was on EntityModule. routeA must hold a reference for path a/detail.

    entity-a.module

    const routeA: Routes = [
      {
        path: 'a',
        component: EntityAComponent,
         children: [
          {
            path: 'detail',
            component: EntityBDetailComponent,
            outlet: 'details',
          },
        ]
      }
    ];