Дочерние маршруты

В прошлых темах рассматривалась работа с обычными маршрутами. То есть система маршрутизации выбирает один из компонентов на основе url и помещает его в главный компонент AppComponent. Но что если выбранный компонент сам должен принимать в качестве внутреннего содержимого какой-то другой компонент в зависимости от запроса. В этому случае нам надо использовать дочерние маршруты (child route).

К примеру, возьмем проект из прошлых тем:



Добавим в папку src/app два простеньких компонента. Вначале добавим файл item.details.component.ts:

import { Component} from '@angular/core';
 
@Component({
    selector: 'item-details',
    template: `<h3>Информация о товаре</h3>`
})
export class ItemDetailsComponent{}

И также добавим второй файл item.stat.component.ts:

import { Component} from '@angular/core';
 
@Component({
    selector: 'item-stat',
    template: `<h3>Статистика товара</h3>`
})
export class ItemStatComponent{}

Итак, есть два компонента ItemDetailsComponent и ItemStatComponent, которые условно раскрывают различные стороны одно и того же товара. Логически они относятся к ItemComponent, который также связан с определенным товаром.

Мы бы могли определить прямые маршруты к этим компонентам типа:

{ path: 'item/:id/details', component: ItemDetailsComponent},
{ path: 'item/:id/stat', component: ItemStatComponent},

Где в данном случае id - также параметр маршрута, представляющий условный id товара.

Но такие маршруты будут миновать компонент ItemComponent и никак его не затрагивают. Поэтому нам надо использовать другую организацию маршрутов. Для этого изменим модуль AppModule следующим образом:

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import {Routes, RouterModule} from '@angular/router';
import { AppComponent }   from './app.component';
import { HomeComponent }   from './home.component';
 
import { ItemComponent }   from './item.component';
import { ItemStatComponent }   from './item.stat.component';
import { ItemDetailsComponent }   from './item.details.component';
 
// определение дочерних маршрутов
const itemRoutes: Routes = [
    { path: 'details', component: ItemDetailsComponent},
    { path: 'stat', component: ItemStatComponent},
];
 
const appRoutes: Routes =[
 
    { path: 'item/:id', component: ItemComponent},
    { path: 'item/:id', component: ItemComponent, children: itemRoutes},
    { path: '', component: HomeComponent}
];
 
@NgModule({
    imports:      [ BrowserModule, RouterModule.forRoot(appRoutes)],
    declarations: [ 
                    AppComponent, HomeComponent, ItemComponent,
                    ItemDetailsComponent, ItemStatComponent
                ],
    bootstrap:    [ AppComponent ]
})
export class AppModule { }

Каждый из дочерних маршрутов сопоставляется не совсем адресом url, а только с его частью. Далее чтобы применить такие маршруты, у маршрута для компонента ItemComponent применяется свойство children:

{ path: 'item/:id', component: ItemComponent, children: itemRoutes},

Теперь изменим ItemComponent:

import { Component} from '@angular/core';
import { ActivatedRoute} from '@angular/router';
import {Subscription} from 'rxjs';
 
@Component({
    selector: 'item-info',
    template: `<h2>Товар {{id}}</h2>
               <router-outlet></router-outlet>`
})
export class ItemComponent{ 
 
    id: number;
    private routeSubscription: Subscription;
 
    constructor(private route: ActivatedRoute){
 
        this.routeSubscription = route.params.subscribe(params=>this.id=params['id']);
    }
}

Для вставки компонентов ItemDetailsComponent и ItemStatComponent здесь определен элемент <router-outlet></router-outlet>.

И изменим главный компонент AppComponent, добавив в него для тестирования ссылки на ItemDetailsComponent и ItemStatComponent:

import { Component} from '@angular/core';
 
@Component({
    selector: 'my-app',
    template: `<div>
                    <nav>
                        <a routerLink="">Главная</a> |
                        <a routerLink="/item/5/details">Информация о товаре</a> |
                        <a routerLink="/item/5/stat">Статистика товара</a>
                    </nav>
                    <router-outlet></router-outlet>
               </div>`
})
export class AppComponent {}

При переходе по подобным ссылкам будет срабатывать маршрутизация к компоненту ItemComponent и ему будет передаваться параметр маршрута - id. И также будет срабатывать маршрутизация к ItemDetailsComponent или ItemStatComponent, если после id идет какой-нибудь сегмент «details» или «stat»: