Create an error handling mechanism that intercepts errors using the displayed information

During my training, I am in the process of creating a small Angular application. One of the tasks I am working on involves implementing an error interceptor based on the guidelines provided in the Angular tutorial. The main goal is to effectively handle errors and ensure that my components continue to receive data through observables.

This is the code for the Interceptor:

    import { Injectable } from '@angular/core';
import {
    HttpRequest,
    HttpHandler,
    HttpEvent,
    HttpInterceptor
} from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { ToastrService } from 'ngx-toastr';

@Injectable({
    providedIn: 'root'
})
export class ErrorInterceptor implements HttpInterceptor {

    constructor(private toastr: ToastrService) { }

    private handleError<T> (result?: T) {
        return (error: any): Observable<T> => {      
          // Log the error on the console.
          console.log(error);    
          // Display user friendly message.
          this.toastr.error(`${error.error.message}`, `${error.statusText}:`);      
          // Let the app keep running by returning an empty result.
          console.log(result);
          return of(result as T);
        };
    }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next
            .handle(request)
            .pipe(
                catchError(this.handleError<any>({data: {token: 'asd'}}))
            );
    }
}

This is the code for the Service:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { Subscription, Observable, of, throwError } from 'rxjs';
import { tap, map, catchError } from 'rxjs/operators';

import { LoginInput } from '../models/login.input-model';
import { RegisterInput } from '../models/register.input-model';
import { ServerResponse } from '../../../core/models/server-response.model';
import { ToastrService } from 'ngx-toastr';

const root = '/api/';

@Injectable({
providedIn: 'root'
})
export class AuthService {
    private loginUrl: string = root + 'auth/login';
    private registerUrl: string = root + 'auth/signup';
    private login$: Subscription;
    private register$: Subscription;

constructor(
    private http: HttpClient,
    private router: Router,
    private toastr: ToastrService) { }


private saveToken(data) {
    localStorage.setItem('token', data.token);
}

login(payload: LoginInput): void {
    this.login$ = this.http
        .post<ServerResponse>(this.loginUrl, payload)
        .subscribe((loginData) => {
            console.log('loginData: ' + loginData);
            // this.router.navigate(['/home']);
        });
}

clearLoginSubscription() {
    if (this.login$) this.login$.unsubscribe();
}    

Desired outcome: The error should be intercepted and returned as an observable. Subscribing to login/register methods should then emit the data. Current issue: The error is intercepted, but no further action takes place. Data is not emitted during login/register subscription.

Note: After experimenting, I found out that moving the handleError function to the service and piping it in a similar way to how it's done in the interceptor produces the desired outcome. However, the same approach does not work when implemented within the interceptor itself.

Answer №1

To capture data, you can use the following code:

this.http
        .post<ServerResponse>(this.loginUrl, payload)
        .subscribe((loginData) => {
            console.log('loginData: ' + loginData);
            // this.router.navigate(['/home']);
        }, (err: HttpErrorReponse) => {
          console.log(err);
        };

http-intercepter.ts

    /**
     * 
     * @param req - parameter to handle http request
     * @param next - parameter for http handler
     */
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const started = Date.now();
        /**
         * Handle newly created request with updated header (if given)
         */
        return next.handle(req).do((event: HttpEvent<any>) => {
            /**
             * Sucessfull Http Response Time.
             */
            if (event instanceof HttpResponse) {
                const elapsed = Date.now() - started;
            }

        }, (err: any) => {
            /**
             * redirect to the error_handler route according to error status or error_code
             * or show a modal
             */
            if (err instanceof HttpErrorResponse) {
                console.log(err)
            }
        });
    }

Answer №2

If you're looking to manage errors and then pass them along, consider utilizing the Catch and Rethrow Strategy.

Make sure your handleError function throws the error:

return throwError(result);

By following this approach, your component can handle data in the error callback instead of the success callback of subscribe().

Check out more information here:

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Angular 2 User Interface with Drag-and-Drop Functionality

I have been searching for a solution that would allow me to drag HTML elements and place them anywhere on the screen. After exploring different options, I came across 2 packages: https://github.com/valor-software/ng2-dragula https://github.com/akser ...

Employ Jade as the template engine for your Angular 2 project

As I transition to developing MEAN apps with Angular2 instead of Angular1.x, I am encountering a challenge involving the use of jade/pug as my template engine in angular2. I came across a tutorial on how to implement this using webpack, but it seems to b ...

Retrieve the browser's language using a Service during initialization

Upon launching the application, I need to obtain the browser's language. To achieve this, I have created a service with the following code: import { Injectable } from '@angular/core'; import { TranslateService } from 'ng2-translate/ng2 ...

What is the best way to merge import all with different elements?

My TSLint is flagging the following issue: Multiple imports from 'moment' can be combined into one. Here is the code causing the problem: import * as moment from 'moment'; import { Moment } from 'moment'; I have attempted ...

Unable to locate the JSON file in the req.body after sending it through an HTTP post request

I have been working on implementing a new feature in my application that involves passing a JSON file from an Angular frontend to a Node backend using Express. The initial code reference can be found at How do I write a JSON object to file via Node server? ...

Site breaks down post transition to Angular Universal (SSR)

Apologies in advance for the ambiguous title, I didn't want to make it too lengthy. So here's my issue: I have a confirmation page that appears after the user completes a payment on a third-party gateway (like PayPal). The page redirects back to ...

Discover the process of transitioning your animations from Angular to CSS

I have successfully implemented a fade-in/out animation using @angular/animation, but now I am looking to transfer this animation to CSS and eliminate the dependency on @angular/animation. Here is my current animation setup (triggering it with [@fadeInOut ...

Transform "<Mutation>" to useMutation

In my removeUser page, I have implemented a < Mutation > and handled errors using the submitForm() function. The initial code worked perfectly: export default function RemoveUserPage() { const [isSubmitted, setIsSubmitted] = useState(false); con ...

Navigating to the top of the page with Angular 5 tab scrolling

Currently, I am facing an issue with the Angular 5 Tabs. Whenever I switch from one tab to another, the page automatically scrolls to the top. Is there anyone familiar with a solution to this problem? <div class="row"> <div class="col-md-12"> ...

What is the best way to run ng test for all components within a sub module of an Angular application?

In my Angular application, I have defined the app module as follows: app.module.ts export class AppModule {} I am able to execute tests using ng test MyApp Within this application, I also have multiple modules set up like so: my-one.module.ts / my-oth ...

How can I call a class method from component.ts in Angular?

Currently tackling an angular project that involves implementing a feature where a button hides all series except for a specific one on a highchart. I'm relatively new to web development and still learning the ropes, so some concepts are a bit challen ...

Tips on adjusting the Leaflet Map's zoom level to display all markers in React Leaflet

I am currently working on a project with React Leaflet map that requires changing the center and zoom based on a set of markers. The goal is to adjust the zoom level so that all the markers are visible on the map. To achieve this change in view, I am util ...

Setting character limits when defining string variables in TypeScript

Upon reviewing the documentation, it appears that there is no straightforward method to perform type checking for the minimum and maximum length of a string data type. However, is there a possible way to define a string data type using custom types in ord ...

TypeScript PatchBaseline with AWS CDK

I am currently working with the AWS CDK and TypeScript, utilizing the @aws-cdk/aws-ssm library to create a PatchBaseline. While I have successfully created the Patch baseline, I'm encountering difficulties when attempting to define approvalRules. I ca ...

I am having trouble getting the guide for setting up a NextJS app with Typescript to function properly

For some time now, I have been experimenting with integrating Typescript into my NextJS projects. Initially, I believed that getting started with Typescript would be the most challenging part, but it turns out that installing it is proving to be even more ...

Angular - Organized hierarchy drag-and-drop functionality

Currently, I am working on an Angular application where I aim to incorporate a nested drag and drop feature with the ability to sort items within their own list. You can find a helpful StackBlitz example demonstrating how to implement nested drag and drop ...

Retrieving the necessary data from my object to perform a sum calculation in angular

Having trouble retrieving an attribute from an array in my code. In my .ts file, I am fetching data from my backend endpoint like this: export class PostFeedComponent implements OnInit { data: any = {}; constructor(private http: HttpClient) { t ...

Inputting data into a static object without changing its keys' data type

Consider the following interface and object: interface Person { name: string; lastName?: string; } const obj: { [key: string]: Person } = { a: { name: 'John' }, b: { name: 'Jane', lastName: 'Doe' }, } This structure ...

Angular Form customizable field

I'm trying to figure out how to create an angular form with a dynamic step. Currently, my form in TypeScript looks like this: this.registerForm = new FormGroup({ role: new FormControl('', [ Validators.required, ]), firstName: ...

Is there a way to verify in Angular whether an image link has a width and height exceeding 1000?

I'm currently working on a function that checks if an image linked in an input field has a width and height greater than 1000 pixels, and is in JPG format. Here's my approach: HTML: <input (change)="checkValidImage(1, product.main_photo)" [ ...