Error handling is a crucial aspect of any application, and Angular provides robust mechanisms to handle errors effectively, especially when dealing with HTTP requests. In this section, we will cover the following topics:

  1. Understanding Error Handling in Angular
  2. Handling HTTP Errors
  3. Global Error Handling
  4. Practical Examples
  5. Exercises

Understanding Error Handling in Angular

Error handling in Angular can be broadly categorized into two types:

  • Synchronous Errors: These are errors that occur during the execution of code, such as syntax errors or logical errors.
  • Asynchronous Errors: These are errors that occur during asynchronous operations, such as HTTP requests.

Angular provides several tools and techniques to handle both types of errors effectively.

Handling HTTP Errors

When making HTTP requests, errors can occur due to various reasons such as network issues, server errors, or client-side errors. Angular's HttpClient module provides mechanisms to handle these errors gracefully.

Using catchError Operator

The catchError operator from RxJS can be used to handle errors in HTTP requests. Here's an example:

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  private apiUrl = 'https://api.example.com/data';

  constructor(private http: HttpClient) {}

  getData(): Observable<any> {
    return this.http.get<any>(this.apiUrl).pipe(
      catchError(this.handleError)
    );
  }

  private handleError(error: HttpErrorResponse) {
    let errorMessage = '';
    if (error.error instanceof ErrorEvent) {
      // Client-side or network error
      errorMessage = `Client-side error: ${error.error.message}`;
    } else {
      // Server-side error
      errorMessage = `Server-side error: ${error.status} - ${error.message}`;
    }
    console.error(errorMessage);
    return throwError(errorMessage);
  }
}

Explanation

  • catchError Operator: This operator intercepts an Observable that has encountered an error and allows you to handle it.
  • handleError Method: This method processes the error and returns a user-friendly error message.

Global Error Handling

Angular provides a way to handle errors globally using the ErrorHandler class. You can create a custom error handler by extending this class.

Custom Error Handler

import { ErrorHandler, Injectable } from '@angular/core';

@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
  handleError(error: any): void {
    // Custom error handling logic
    console.error('An error occurred:', error.message);
    // Optionally, you can log the error to an external server
  }
}

Registering the Custom Error Handler

You need to register your custom error handler in the AppModule.

import { NgModule, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { GlobalErrorHandler } from './global-error-handler';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  providers: [{ provide: ErrorHandler, useClass: GlobalErrorHandler }],
  bootstrap: [AppComponent]
})
export class AppModule {}

Practical Examples

Example 1: Handling HTTP Errors in a Component

import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-data',
  template: `
    <div *ngIf="errorMessage">{{ errorMessage }}</div>
    <div *ngIf="data">{{ data | json }}</div>
  `
})
export class DataComponent implements OnInit {
  data: any;
  errorMessage: string;

  constructor(private dataService: DataService) {}

  ngOnInit(): void {
    this.dataService.getData().subscribe(
      (data) => (this.data = data),
      (error) => (this.errorMessage = error)
    );
  }
}

Example 2: Global Error Handling

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `<h1>Angular Global Error Handling</h1>`
})
export class AppComponent {
  constructor() {
    // Simulate an error
    throw new Error('Test error');
  }
}

Exercises

Exercise 1: Handling HTTP Errors

  1. Create a new service that makes an HTTP GET request to a non-existent endpoint.
  2. Implement error handling using the catchError operator.
  3. Display the error message in a component.

Solution

// data.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  private apiUrl = 'https://api.example.com/non-existent-endpoint';

  constructor(private http: HttpClient) {}

  getData(): Observable<any> {
    return this.http.get<any>(this.apiUrl).pipe(
      catchError(this.handleError)
    );
  }

  private handleError(error: HttpErrorResponse) {
    let errorMessage = '';
    if (error.error instanceof ErrorEvent) {
      errorMessage = `Client-side error: ${error.error.message}`;
    } else {
      errorMessage = `Server-side error: ${error.status} - ${error.message}`;
    }
    console.error(errorMessage);
    return throwError(errorMessage);
  }
}

// data.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-data',
  template: `
    <div *ngIf="errorMessage">{{ errorMessage }}</div>
    <div *ngIf="data">{{ data | json }}</div>
  `
})
export class DataComponent implements OnInit {
  data: any;
  errorMessage: string;

  constructor(private dataService: DataService) {}

  ngOnInit(): void {
    this.dataService.getData().subscribe(
      (data) => (this.data = data),
      (error) => (this.errorMessage = error)
    );
  }
}

Exercise 2: Global Error Handling

  1. Create a custom error handler that logs errors to the console.
  2. Register the custom error handler in the AppModule.
  3. Simulate an error in a component to test the global error handler.

Solution

// global-error-handler.ts
import { ErrorHandler, Injectable } from '@angular/core';

@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
  handleError(error: any): void {
    console.error('An error occurred:', error.message);
  }
}

// app.module.ts
import { NgModule, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { GlobalErrorHandler } from './global-error-handler';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  providers: [{ provide: ErrorHandler, useClass: GlobalErrorHandler }],
  bootstrap: [AppComponent]
})
export class AppModule {}

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `<h1>Angular Global Error Handling</h1>`
})
export class AppComponent {
  constructor() {
    throw new Error('Test error');
  }
}

Conclusion

In this section, we covered the essentials of error handling in Angular, focusing on handling HTTP errors and implementing global error handling. By using the catchError operator and creating custom error handlers, you can ensure that your Angular applications handle errors gracefully and provide a better user experience. In the next module, we will explore state management in Angular.

© Copyright 2024. All rights reserved