Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

custom error handler throw error : Cannot read property 'get' of undefined ( Injector )

I am building a custom error handler in angular 4 to handle different kind of application error with error interceptor

create a base class( app-error.ts ) and other classes ( for eg. to handle 403 errors create class access-denied.ts ) which extend this base class.

in the base class have injected a service toastrService and I want to display custom message from the child class BUT it gives the error on

Cannot read property 'get' of undefined

thisissue is related to OOPS concept. I do not understand how to get override parent method or call with my custom argument.

TS v 2.3.3 angular v 4.3.4

app.module.ts

providers: [
{ provide: ErrorHandler, useClass: AppErrorHandler }
]

NOTE: AppErrorHandler class is completely different than AppError which extend angular ErorHandler interface which handle system errors.

error-interceptor

import { Router } from '@angular/router';
import { Injectable, Injector } from '@angular/core';
import { HttpInterceptor, HttpResponse, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse
} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';

import {
    AuthFail,
    BadInput,
    NotFoundError,
    ServerError,
    AppError,
    AccessDenied,
} from '../shared/errors';
import { AuthenticationService } from './../authentication/authentication.service';


@Injectable()
export class ErrorInterceptor implements HttpInterceptor {

    private auth: AuthenticationService;
    constructor(private router: Router, private injector: Injector) { }
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const auth = this.injector.get(AuthenticationService);
        return next.handle(req).catch((err: HttpErrorResponse) => {
            if (err instanceof HttpErrorResponse) {
                if (err.status === 401) {
                    return Observable.throw(new AuthFail(err.error));
                }
                if (err.status === 400) {
                    return Observable.throw(new BadInput(err.error));
                }
                if (err.status === 404) {
                    return Observable.throw(new NotFoundError());
                }
                if (err.status === 403) {
                    return Observable.throw(new AccessDenied());
                }
                return Observable.throw(new AppError(err));
            }
        });
    }
}

access-denied.ts

import { AppError } from './app-error';
export class AccessDenied extends AppError {
    constructor(public originalError?: any) {
        super();
        console.log('inside acces denied constructor');
        // super.handleError("superrrrr"); // this also doesn't work
    }

    public handleError(): void {
        console.log('handleError: ', );
        super.handleError("Access denined error occured");
    }
}

app-error.ts

import { Inject, Injector } from "@angular/core";

import { ToastrService } from "ngx-toastr";

export class AppError {
    toastrService: ToastrService;
    constructor(public originalError?: any, private injector?: Injector) {
        this.toastrService = this.injector.get(ToastrService);
    }

    // NOTE: using getter seems impossible to access in child so add the same in constructor
    // get toastrService(): ToastrService {
    //  return this.injector.get(ToastrService);
    // }

    public handleError(msg: string): void {
        this.toastrService.error(`Error Message: ${msg}`,
            "Error", {
                closeButton: true,
                timeOut: 5000,
                onActivateTick: true
            }
        );
    }

}

gives error


core.es5.js:1020 ERROR TypeError: Cannot read property 'get' of undefined 
    at AccessDenied.AppError (app-error.ts:8) 
    at new AccessDenied (access-denied.ts:6)  
    at CatchSubscriber.eval [as selector] (error.interceptor.ts:61) 
    at CatchSubscriber.error (catchError.js:105)
    at XMLHttpRequest.onLoad (http.es5.js:1739)
    at ZoneDelegate.invokeTask (zone.js:421)
    at Object.onInvokeTask (core.es5.js:3881)
    at ZoneDelegate.invokeTask (zone.js:420)
    at Zone.runTask (zone.js:188)
    at ZoneTask.invokeTask [as invoke] (zone.js:496)
like image 368
diEcho Avatar asked Nov 08 '22 09:11

diEcho


1 Answers

AccessDenied incorrectly extends AppError. super() results in injector being undefined, and injector shouldn't be optional in AppError constructor, because it is required.

It could be fixed by making a parameter obligatory:

constructor(private injector: Injector, public originalError?: any) {
    this.toastrService = this.injector.get(ToastrService);
}

The constructor can be omitted in AccessDenied. And should be instantiated like new AccessDenied(injector).

The real problem here is that AppError does the work it isn't supposed to to. Considering that it just contains the error that can be later determined with err instanceof AppError, it shouldn't do side effects that are currently done in handleError.

The logic in handleError could be moved to a method in ToastrService or separate error service that accepts an instance of AppError. If there's a need to provide default message for error type like Access denied error occurred, AppError can have public property that contains the message.

like image 81
Estus Flask Avatar answered Nov 14 '22 23:11

Estus Flask