Как обрабатывать ошибки в Angular?
В Angular ошибки могут возникать в различных контекстах: при выполнении HTTP-запросов, в жизненном цикле компонентов, в шаблонах, при загрузке маршрутов и модулей, а также на уровне глобального приложения. Обработка ошибок — это важная часть архитектуры любого Angular-приложения, особенно для обеспечения стабильности, информативности и улучшения пользовательского опыта. Angular предоставляет несколько механизмов для локальной и глобальной обработки ошибок.
1. Обработка ошибок в HTTP-запросах
1.1. Оператор catchError (RxJS)
Для перехвата ошибок HTTP-запросов используется оператор catchError из RxJS:
import { catchError } from 'rxjs/operators';
import { throwError, of } from 'rxjs';
this.http.get('/api/data').pipe(
catchError(error => {
console.error('Ошибка:', error);
return throwError(() => error);
})
).subscribe({
next: data => console.log(data),
error: err => console.log('Обработано в subscribe')
});
1.2. Возврат альтернативного значения
.pipe(
catchError(error => {
return of(\[\]); // Возврат безопасного значения
})
)
1.3. Повтор запроса при ошибке
import { retry } from 'rxjs/operators';
this.http.get('/api/data')
.pipe(retry(3))
.subscribe();
2. Глобальный перехват HTTP-ошибок (интерсептор)
Создание HttpInterceptor для глобального логгирования и перенаправлений:
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';
import { Observable, catchError, throwError } from 'rxjs';
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401) {
// перенаправить на логин
}
console.error('HTTP Error:', error);
return throwError(() => error);
})
);
}
}
Регистрация:
providers: \[
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true }
\]
3. Глобальная обработка ошибок — ErrorHandler
Angular предоставляет класс ErrorHandler для перехвата необработанных исключений:
import { ErrorHandler, Injectable } from '@angular/core';
@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
handleError(error: any): void {
console.error('Глобальная ошибка:', error);
// логирование на сервер
// уведомление пользователя
}
}
Регистрация:
providers: \[
{ provide: ErrorHandler, useClass: GlobalErrorHandler }
\]
Этот обработчик перехватывает ошибки, которые не были пойманы внутри try/catch или Observable.catchError.
4. Обработка ошибок в шаблоне
В шаблоне Angular не выбрасывает ошибки напрямую, но можно использовать ngIf, try/catch в pipe-функциях и безопасную навигацию:
<div \*ngIf="user?.profile?.name">{{ user.profile.name }}</div>
Также можно использовать кастомные пайпы с безопасной логикой:
@Pipe({ name: 'safeJson' })
export class SafeJsonPipe implements PipeTransform {
transform(value: string): any {
try {
return JSON.parse(value);
} catch {
return null;
}
}
}
5. Ошибки в ngOnInit и других хуках
Ошибки в lifecycle-хуках можно обрабатывать через try/catch:
ngOnInit() {
try {
this.loadData();
} catch (e) {
console.error('Ошибка в ngOnInit:', e);
}
}
Однако Angular всё равно прокинет ошибку выше, если она не была поймана. Лучше использовать RxJS и обработку через catchError или tap.
6. Обработка ошибок загрузки маршрутов (Lazy Loading)
При ошибках при ленивой загрузке модулей можно использовать RouterModule с ErrorHandler или ловить ошибки в навигации:
this.router.events.subscribe(event => {
if (event instanceof NavigationError) {
console.error('Ошибка загрузки маршрута:', event.error);
}
});
7. Обработка ошибок форм
Template-driven:
<input name="email" \[(ngModel)\]="email" required email #emailRef="ngModel" />
<div \*ngIf="emailRef.errors?.required">Email обязателен</div>
<div \*ngIf="emailRef.errors?.email">Некорректный email</div>
Reactive forms:
this.form = this.fb.group({
email: \['', \[Validators.required, Validators.email\]\]
});
<input formControlName="email" />
<div \*ngIf="form.get('email')?.hasError('required')">Обязательное поле</div>
<div \*ngIf="form.get('email')?.hasError('email')">Некорректный email</div>
8. Вывод ошибок в UI
Можно использовать глобальные сервисы для отображения ошибок пользователю:
@Injectable({ providedIn: 'root' })
export class NotificationService {
showError(message: string) {
// отображение через toast, snackbar или модалку
}
}
Интеграция с перехватчиком:
catchError(error => {
this.notifier.showError('Произошла ошибка');
return throwError(() => error);
})
9. Логгирование на сервер
Часто глобальный ErrorHandler используется для отправки ошибок на внешний сервер логов:
this.http.post('/log-error', {
message: error.message,
stack: error.stack
}).subscribe();
10. Использование try/catch с await
В асинхронных методах:
async loadData() {
try {
const data = await this.http.get('/api/data').toPromise();
} catch (e) {
console.error('Ошибка при загрузке данных:', e);
}
}
11. Перехват ошибок при инициализации приложения (APP_INITIALIZER)
Если приложение требует асинхронной инициализации, ошибки в APP_INITIALIZER могут быть фатальными:
{
provide: APP_INITIALIZER,
useFactory: (configService: ConfigService) => () => configService.load().catch(err => {
console.error('Ошибка инициализации', err);
return Promise.resolve(); // предотвращает сбой загрузки
}),
deps: \[ConfigService\],
multi: true
}
12. Angular Zones и ошибки
Angular использует NgZone для контроля изменений. Ошибки вне зоны (например, из сторонних библиотек) могут быть не задетектированы. Можно явно запускать логику в зоне:
this.zone.run(() => {
this.data = updatedValue;
});
13. Инструменты
-
Sentry, LogRocket, Rollbar — для автоматического сбора фронтенд-ошибок.
-
console.error, Debugger, window.onerror, Error.stack — для локальной отладки.
14. Поведение throwError
Начиная с RxJS 7:
return throwError(() => new Error('Ошибка'));
Передача функции, а не объекта, обязательна для правильной работы.
15. Навигация по ошибке
Если возникает ошибка — можно выполнить редирект:
catchError(error => {
this.router.navigate(\['/error-page'\]);
return throwError(() => error);
})
Angular предоставляет как локальные, так и централизованные инструменты для надёжной и гибкой обработки ошибок. Использование перехватчиков, глобального ErrorHandler, реактивного подхода с catchError, и UI-уведомлений — основа продуманной стратегии обработки ошибок.