Как передать данные в Angular routed components: полное руководство на TypeScript
Каждый разработчик, который начинает работать с Angular, рано или поздно сталкивается с вопросом: «Как передать данные из одного компонента в другой через роутер?». На Stack Overflow этот вопрос собрал тысячи просмотров — и это не случайно. В отличие от обычных parent-child компонентов, где достаточно использовать @Input() и @Output(), роутинг в Angular добавляет свои сложности.
В этой статье мы разберём 5 проверенных способов передачи данных в Angular routed components. Вы узнаете, когда какой метод использовать, и получите готовые примеры на TypeScript. Поехали!
Почему нельзя просто передать данные через @Input?
Многие новички пытаются сделать так:
// ❌ Так не работает
<router-outlet [someData]="data"></router-outlet>
Angular Router создаёт компоненты динамически. router-outlet — это директива, а не шаблонный элемент. Поэтому передача данных напрямую через атрибуты невозможна. Но есть обходные пути — и они даже удобнее!
Способ 1: Использование Router State (самый простой)
Самый интуитивный способ — передать данные через объект state при навигации. Этот метод появился в Angular 7+ и работает «из коробки».
Как это работает
Вы передаёте объект с данными при вызове router.navigate() или через routerLink.
// Component A — отправитель
import { Component } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-user-list',
template: `
<button (click)="goToDetails()">Открыть пользователя</button>
`
})
export class UserListComponent {
constructor(private router: Router) {}
goToDetails() {
this.router.navigate(['/user', 42], {
state: { name: 'Иван Петров', role: 'admin' }
});
}
}
// Component B — получатель
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-user-details',
template: `
<div *ngIf="userData">
<h2>{{ userData.name }}</h2>
<p>Роль: {{ userData.role }}</p>
</div>
`
})
export class UserDetailsComponent implements OnInit {
userData: any;
constructor(private router: Router) {
// Получаем данные из истории навигации
this.userData = this.router.getCurrentNavigation()?.extras?.state;
}
ngOnInit() {
// Альтернативный способ — через history.state
if (!this.userData) {
this.userData = history.state;
}
}
}
Плюсы и минусы
- ✅ Простота реализации
- ✅ Данные не видны в URL
- ✅ Работает с любыми типами данных (объекты, массивы)
- ❌ Данные теряются при обновлении страницы
- ❌ Не работает при прямом переходе по URL
Когда использовать: для передачи временных данных, которые не критичны при перезагрузке (например, flash-сообщения, фильтры).
Способ 2: Route Resolver (для предзагрузки данных)
Если вам нужно загрузить данные до того, как компонент отобразится, используйте Angular Resolver. Это сервис, который резолвит данные перед активацией маршрута.
Создаём Resolver
// user.resolver.ts
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
import { Observable, of } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class UserResolver implements Resolve<any> {
resolve(route: ActivatedRouteSnapshot): Observable<any> {
const userId = route.paramMap.get('id');
// Имитация HTTP-запроса
return of({
id: userId,
name: 'Мария Соколова',
email: 'maria@example.com'
});
}
}
Настройка маршрута
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { UserDetailsComponent } from './user-details/user-details.component';
import { UserResolver } from './resolvers/user.resolver';
const routes: Routes = [
{
path: 'user/:id',
component: UserDetailsComponent,
resolve: { userData: UserResolver }
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Получение данных в компоненте
// user-details.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-user-details',
template: `
<h2>{{ user?.name }}</h2>
<p>Email: {{ user?.email }}</p>
`
})
export class UserDetailsComponent implements OnInit {
user: any;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.user = this.route.snapshot.data['userData'];
// Или подписка на изменения:
// this.route.data.subscribe(data => this.user = data['userData']);
}
}
Плюсы и минусы
- ✅ Данные загружаются до рендера компонента
- ✅ Легко тестировать и переиспользовать
- ✅ Работает при прямом переходе по URL
- ❌ Требуется больше кода (отдельный сервис)
- ❌ Избыточно для простых данных
Когда использовать: когда компоненту нужны данные с сервера перед отображением (профиль пользователя, детали товара).
Способ 3: Query Parameters и Fragment (данные в URL)
Самый «веб-способ» — передавать данные через query-параметры (?key=value) или фрагменты (#section). Данные видны в URL, что удобно для шаринга.
Пример с query params
// Отправитель
this.router.navigate(['/search'], {
queryParams: { query: 'Angular', page: 2, sort: 'date' }
});
// Получатель
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-search',
template: `
<p>Поиск: {{ query }}</p>
<p>Страница: {{ page }}</p>
`
})
export class SearchComponent implements OnInit {
query: string = '';
page: number = 1;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.route.queryParams.subscribe(params => {
this.query = params['query'] || '';
this.page = +params['page'] || 1;
});
}
}
Fragment (якорь)
// Переход с фрагментом
this.router.navigate(['/docs'], { fragment: 'installation' });
// Получение фрагмента
this.route.fragment.subscribe(frag => {
console.log('Фрагмент:', frag);
// Прокрутка к элементу
document.getElementById(frag)?.scroll