Learn how to structure, customize, and display error handling in your Angular project effectively.
Introduction
Error handling is a critical aspect of any Angular application. It ensures that your app can gracefully handle unexpected issues, providing a better user experience and making debugging easier for developers. In this blog, we'll explore how to structure error handling in your Angular project, create customized error handlers, and the best ways to display errors to users.
Structuring Error Handling in Your Angular Project
To maintain a clean and scalable codebase, it's essential to structure your error handling logic properly. Here's a recommended project hierarchy for error handling:
src/ ├── app/ │ ├── core/ │ │ ├── errors/ │ │ │ ├── error-handler.service.ts │ │ │ ├── global-error-handler.ts │ │ │ ├── app-error.model.ts │ ├── shared/ │ │ ├── components/ │ │ │ ├── error-display/ │ │ │ │ ├── error-display.component.ts │ │ │ │ ├── error-display.component.html │ │ │ │ ├── error-display.component.css
This structure ensures that error handling logic is centralized and reusable across your application.
Implementation
Below are examples of how you might implement error-handler.service.ts, global-error-handler.ts, and error.model.ts in an Angular application.
1. error.model.ts
export interface AppError {
message: string;
statusCode?: number;
details?: string;
timestamp?: Date;
}
export class AppErrorImpl implements AppError {
message: string;
statusCode?: number;
details?: string;
timestamp?: Date;
constructor(message: string, statusCode?: number, details?: string) {
this.message = message;
this.statusCode = statusCode;
this.details = details;
this.timestamp = new Date();
}
}
2. error-handler.service.ts
import { Injectable } from '@angular/core';
import { AppError, AppErrorImpl } from './error.model';
import { LoggingService } from '../../shared/services/logging.service'; // Assuming you have a logging service
@Injectable({
providedIn: 'root',
})
export class ErrorHandlerService {
constructor(private loggingService: LoggingService) {}
handleError(error: AppError): void {
// Log the error using a logging service
this.loggingService.logError(error);
// You can also add additional error handling logic here
// For example, sending the error to a remote server
}
createError(message: string, statusCode?: number, details?: string): AppError {
return new AppErrorImpl(message, statusCode, details);
}
}
3. global-error-handler.ts
import { ErrorHandler, Injectable } from '@angular/core';
import { ErrorHandlerService } from './error-handler.service';
import { AppErrorImpl } from './error.model';
@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
constructor(private errorHandlerService: ErrorHandlerService) {}
handleError(error: any): void {
// Convert the error to a known AppError format
const appError = new AppErrorImpl(
error.message || 'An unexpected error occurred',
error.status || 500,
error.stack
);
// Handle the error using the ErrorHandlerService
this.errorHandlerService.handleError(appError);
// Optionally, you can rethrow the error or perform other actions
// throw error;
}
}
4. Registering the Global Error Handler
import { NgModule, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { GlobalErrorHandler } from './core/errors/global-error-handler';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [
{
provide: ErrorHandler,
useClass: GlobalErrorHandler,
},
],
bootstrap: [AppComponent],
})
export class AppModule {}
5. Example Usage in a Component
import { Component } from '@angular/core';
import { ErrorHandlerService } from '../core/errors/error-handler.service';
@Component({
selector: 'app-some-component',
templateUrl: './some-component.component.html',
styleUrls: ['./some-component.component.css'],
})
export class SomeComponent {
constructor(private errorHandlerService: ErrorHandlerService) {}
someMethod() {
try {
// Some code that might throw an error
} catch (error) {
this.errorHandlerService.handleError(
this.errorHandlerService.createError('An error occurred in someMethod', 500, error.stack)
);
}
}
}
Displaying Errors to Users
How you display errors to users can significantly impact their experience. Here are some best practices:
- Use Toast Notifications: Display non-intrusive toast messages for minor errors.
- Modal Dialogs: Use modal dialogs for critical errors that require user attention.
- Inline Error Messages: Show validation errors directly in forms.
Here's an example of a reusable error display component:
<div *ngIf="errorMessage" class="error-display"> <p>{{ errorMessage }}</p> <button (click)="clearError()">Dismiss</button> </div>
And its corresponding TypeScript code:
import { Component, Input } from '@angular/core'; @Component({ selector: 'app-error-display', templateUrl: './error-display.component.html', styleUrls: ['./error-display.component.css'], }) export class ErrorDisplayComponent { @Input() errorMessage: string | null = null; clearError(): void { this.errorMessage = null; } }
Conclusion
Proper error handling is essential for building robust Angular applications. By structuring your error handling logic, creating custom error handlers, and displaying errors effectively, you can improve both the user experience and the maintainability of your code. Start implementing these practices in your project today!
0 Comments