Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular: Error handling - Interceptors and modals

I have built an Angular 5 application which is handling errors on every single call i make. Using the HttpClient i am able to intercept the error that happens after a request to the server has been sent. The error is intercepted on a service method that send the request to the API and then when an error happens it is pushed across to the component in order to display a nice modal with the error message.

I am want to use interceptors to achieve the same behaviour in order to handle all errors on one single centralised way. But I am not sure if it is possible to communicate with the component from the interceptor class, send the message to it, so it can trigger the modal as it is currently doing, or How can I trigger the modal directly from the interceptor class.

This is my current logic:

The component:

  ....
 export class VerificationComponent implements OnInit {
  //Use to call the modal when errors happen
  @ViewChild('modalError') displayErrorRef: ModalComponent; 

//Method send a request to the api using a service instance _myService
getNextRecord() {

    this.errorMesage = "";
    this._myService.getCandidate()
        .subscribe(candidate => {
            this.loadCandidate = candidate;
            this.userName = this.appService.getUser();
        }, error => {                
            this.errorMesage = <any>error.errorMessage;                
            this.displayErrorRef.show();
        });
   }

}
....

The service:

.....
@Injectable()

export class MyService {

  getCandidate(): Observable<ICandidate> {
    return this._http.get(this._getCandidateUrl, this.jwt())
        .map((response: Response) => <ICandidate>response.json())            
        .catch(this.handleError);
   }


private handleError(error: Response) {        
    if (error.text())
        return Observable.throw({errorMessage:error.text(),erroStatus: error.status });
    else
        return Observable.throw('You are not authorised to get this resource');
    }
}
....

The template:

 <!-- This is a child component to display the error message on the top of 
 this template -->
  .....
<app-modal #modalError>
<div class="app-modal-header">
    Error
</div>
<div class="app-modal-body">
    {{errorMesage}}
</div>
<div class="app-modal-footer">
    <button type="button" class="btn btn-default" (click)="hideModalError()">Logout</button>
    <button type="button" class="btn btn-default"(click)="tryGetNextRecord()">Try Again</button>
</div>
</app-modal>
....
like image 500
D.B Avatar asked Jun 25 '26 12:06

D.B


1 Answers

For a global error handler example that uses an HttpInterceptor you need the following.

  • ErrorInterceptor
  • ErrorService
  • CustomErrorModal

The flow is:

app.module => register interceptor.

app.module => regsiter error service as a provider.

app.component => register global error handling that show the error modal.

YourCustomComponent => do not subscribe to the error of the Subject/Observable

Your main app.component will subscribe to any updates from the error service and display them accordingly using the modal ref.

app.module

//other code
const interceptors = [{
    provide: HTTP_INTERCEPTORS,
    useClass: ErrorInterceptor,
    multi: true
}];

const services = [{
    ErrorService
}];

@NgModule({
    //other code
    providers: [
        interceptors,
        services
    ],
    //other code
})

error.service

@Injectable()
export class ErrorService
{
    private errors = new Subject<string[]>();

    constructor() { }

    public addErrors = (errors: string[]): void =>
        this.errors.next(errors);

    public getErrors = () =>
        this.errors.asObservable();
}

error.interceptor

@Injectable()
export class ErrorInterceptor implements HttpInterceptor
{
    constructor(private errorService: ErrorService)
    {         
    }

    intercept(
        request: HttpRequest<any>,
        next: HttpHandler): Observable<HttpEvent<any>>
    {
        return next.handle(request).do(() => { }, (response) =>
        {
            if (response instanceof HttpErrorResponse)
            {                
                if (response.status === 401)
                {
                    return;
                }                

                if (response.status === 400 &&
                    response.error)
                {
                    this.errorService.addErrors(Array.isArray(response.error) ? response.error : [response.error]);
                    return;
                }

                this.errorService.addErrors([`Your generic error message`]);
            }

            return Observable.throw(response);
        });
    }
}

app.component

export class AppComponent implements OnDestroy
{
    private ngUnsubscribe = new Subject();

    @ViewChild('modalError')
    displayErrorRef: ModalComponent; 

    constructor(private errorService: ErrorService)
    {
        this.initializeErrors();
    }    

    ngOnDestroy()
    {
        this.ngUnsubscribe.next();
        this.ngUnsubscribe.complete();
    }

    private initializeErrors()
    {
        this
            .errorService
            .getErrors()
            .pipe(takeUntil(this.ngUnsubscribe))
            .subscribe((errors) =>
            {
                //this.displayErrorRef.error = errors
                this.displayErrorRef.show();
            });
    }   
}

ngUnsubscribe is to automagicly dispose the subscription when your main app.component gets destroyed.

like image 112
Tjaart van der Walt Avatar answered Jun 28 '26 18:06

Tjaart van der Walt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!