1. Введение
Если бы вы писали интернет-магазин и хотели отображать страницу товара по адресу /product/123, где 123 — идентификатор товара, вам понадобился бы динамический маршрут. То же самое — для пользователей, статей, заказов и всего, что имеет уникальный идентификатор.
В Angular такой маршрут задаётся с помощью синтаксиса /user/:id, где :id — это параметр маршрута (или route parameter). Он работает как переменная: Angular автоматически подставляет в неё значение из URL.
Пример:
- /user/5 — параметр id равен 5
- /user/42 — параметр id равен 42
Вам не нужно создавать отдельный маршрут для каждого пользователя — достаточно одного маршрута с параметром.
Как объявить динамический маршрут
Давайте рассмотрим минимальный пример маршрута с параметром:
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { UserDetailComponent } from './user-detail/user-detail.component';
const routes: Routes = [
{ path: 'user/:id', component: UserDetailComponent },
// ... другие маршруты
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Здесь мы определяем маршрут user/:id, где :id — это имя параметра. Оно может быть любым (например, :userId), главное — одинаково использовать его в маршруте и в коде компонента.
Вопрос на засыпку:
Что будет, если вы перейдёте по адресу /user/123?
Ответ: Angular отобразит компонент UserDetailComponent и передаст ему параметр id со значением 123.
2. Переход по динамическому маршруту
Чтобы попасть на страницу пользователя с определённым ID, вы можете использовать:
routerLink
<!-- user-list.component.html -->
<ul>
<li *ngFor="let user of users">
<a [routerLink]="['/user', user.id]">{{ user.name }}</a>
</li>
</ul>
Если у пользователя id=7, Angular сформирует ссылку /user/7.
В коде (программная навигация)
import { Router } from '@angular/router';
constructor(private router: Router) {}
goToUser(userId: number) {
this.router.navigate(['/user', userId]);
}
3. Получение параметров маршрута в компоненте
Вот тут начинается магия! Ваш компонент должен узнать, какой именно id был передан в URL.
Подключаем ActivatedRoute
ActivatedRoute — это сервис, который содержит информацию о текущем маршруте, включая параметры.
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-user-detail',
template: `
<h2>Пользователь №{{ userId }}</h2>
<!-- Здесь можно вывести другие данные -->
`
})
export class UserDetailComponent implements OnInit {
userId!: string; // или number, если преобразуете
constructor(private route: ActivatedRoute) {}
ngOnInit() {
// Получаем параметр id из URL
this.userId = this.route.snapshot.paramMap.get('id')!;
// Если параметр обязателен, можно не проверять на null
}
}
Объяснение:
this.route.snapshot.paramMap.get('id') — достаёт значение параметра id из URL.
Значение всегда строка. Если нужен number, делаем Number(...).
Почему snapshot?
snapshot — это "снимок" параметров на момент инициализации компонента. Если компонент не пересоздаётся при смене параметра (например, переход /user/5 → /user/6 внутри одного компонента), snapshot не обновится. Тогда нужно подписаться на изменения параметров!
4. Реакция на изменение параметров (Observable)
В реальных приложениях пользователь может находиться на странице пользователя, а затем переходить к другому пользователю (например, через <routerLink>), и Angular не уничтожает компонент, а просто меняет параметр.
Чтобы реагировать на такие изменения, используйте Observable:
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-user-detail',
template: `<h2>Пользователь №{{ userId }}</h2>`
})
export class UserDetailComponent implements OnInit {
userId!: string;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.route.paramMap.subscribe(params => {
this.userId = params.get('id')!;
// Здесь можно, например, подгрузить данные пользователя по id
});
}
}
Комментарий:
paramMap — это Observable, который уведомляет о каждом изменении параметров маршрута.
Не забудьте отписаться, если компонент может быть уничтожен до конца жизни приложения (иначе утечка памяти).
5. Применение: загрузка данных по параметру
Давайте свяжем наш пример с реальной задачей: загрузим данные пользователя из массива или сервиса по id.
// user-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
const USERS = [
{ id: 1, name: 'Алиса' },
{ id: 2, name: 'Боб' },
{ id: 3, name: 'Чарли' }
];
@Component({
selector: 'app-user-detail',
template: `
<div *ngIf="user">
<h2>Пользователь: {{ user.name }}</h2>
<p>ID: {{ user.id }}</p>
</div>
<div *ngIf="!user">
<p>Пользователь не найден</p>
</div>
`
})
export class UserDetailComponent implements OnInit {
user: { id: number, name: string } | undefined;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.route.paramMap.subscribe(params => {
const id = Number(params.get('id'));
this.user = USERS.find(u => u.id === id);
});
}
}
Теперь если вы перейдёте по /user/2, отобразится "Пользователь: Боб".
6. Полезные нюансы
Несколько параметров маршрута
Можно использовать несколько параметров в одном маршруте:
{ path: 'order/:orderId/item/:itemId', component: OrderItemComponent }
В компоненте:
ngOnInit() {
this.route.paramMap.subscribe(params => {
const orderId = params.get('orderId');
const itemId = params.get('itemId');
// Используйте оба параметра!
});
}
Параметры как часть вложенных маршрутов
const routes: Routes = [
{ path: 'user/:userId', component: UserComponent, children: [
{ path: 'profile', component: UserProfileComponent },
{ path: 'settings', component: UserSettingsComponent }
]
}
];
В дочерних компонентах (UserProfileComponent и др.) можно получить параметр userId через цепочку ActivatedRoute:
constructor(private route: ActivatedRoute) {}
ngOnInit() {
// Получить параметр из родительского маршрута:
const userId = this.route.parent?.snapshot.paramMap.get('userId');
}
Отличие route параметров от query-параметров
Route-параметры (/user/:id) — часть пути, обязательны для перехода.
Query-параметры (/user/42?tab=settings) — идут после ?, опциональны, используются для фильтрации, пагинации и т.д.
Query-параметры получаются через this.route.queryParamMap.
7. Типичные ошибки при работе с динамическими маршрутами
Ошибка №1: Неправильное имя параметра.
Если в маршруте написано :id, а в коде вы обращаетесь к params.get('userId'), ничего не найдётся. Имена должны совпадать.
Ошибка №2: Использование snapshot вместо подписки.
Если вы используете только snapshot.paramMap.get(), а компонент не пересоздаётся при смене параметра, данные не обновятся. Для динамических переходов используйте подписку на paramMap.
Ошибка №3: Преобразование типов.
Параметры всегда приходят как строки. Если вам нужен number, не забудьте преобразовать: Number(params.get('id')).
Ошибка №4: Отсутствие проверки на null.
Если параметр не найден, params.get() вернёт null. Лучше подстраховаться, особенно если параметр не обязателен.
Ошибка №5: Отсутствие отписки от Observable.
Если компонент может быть уничтожен до конца жизни приложения, не забудьте отписаться от подписки на paramMap, чтобы избежать утечек памяти. В простых случаях Angular сам всё почистит, но в сложных компонентах лучше явно использовать takeUntil или аналогичные методы.
Ошибка №6: Одинаковые маршруты без параметра и с параметром.
Если у вас есть маршруты /user и /user/:id, порядок их объявления в массиве routes важен! Сначала должен идти более специфичный маршрут (/user/:id), иначе Angular всегда будет попадать на первый подходящий.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ