使用材料设计的Angular2应用程序的通用错误处理程序

使用材料设计的Angular2应用程序的通用错误处理程序,angular,typescript,error-handling,dialog,material-design,Angular,Typescript,Error Handling,Dialog,Material Design,我试图为Angular4应用程序实现一个通用的ErrorHandler,目的是显示一个对话框,其中包含有关我收到的错误的一些信息 这是错误处理程序: import { ErrorHandler, Injectable, Injector } from '@angular/core' // import { Router } from '@angular/router' import { MatDialog } from '@angular/material' import { HttpErro

我试图为Angular4应用程序实现一个通用的ErrorHandler,目的是显示一个对话框,其中包含有关我收到的错误的一些信息

这是错误处理程序:

import { ErrorHandler, Injectable, Injector } from '@angular/core'
// import { Router } from '@angular/router'
import { MatDialog } from '@angular/material'

import { HttpErrorResponse } from '@angular/common/http'

import { MessageDialog } from './message.dialog'
// import { LoginService } from '../login/main.service'


@Injectable()
export class DialogErrorHandler implements ErrorHandler {

    constructor(private injector: Injector) {
    }

    handleError(error: any): void {
        let localError = error;
        let finalMessage: string = "Errore sconosciuto";
        let finalCallback: () => void = () => { console.log("default callback")};

        let dialog: MatDialog = this.injector.get(MatDialog);
        // let login: LoginService = this.injector.get(LoginService);
        // let router: Router = this.injector.get(Router);
        if( localError instanceof HttpErrorResponse && localError.error instanceof Error){
            localError = localError.error
        }
        if (localError instanceof HttpErrorResponse) {
            console.log("http error");
            let errorDesc = localError.status + " " + localError.statusText + ": ";
            switch (localError.status) {
                case 403:
                    finalMessage = "La sessione è scaduta, ripeti il login.";
                    // finalCallback = () => { login.logout(); router.navigate['/login']; };
                    break;
                case 500:
                    finalMessage = "Errore sul server - " + errorDesc + localError.error;
                    break;
                default:
                    finalMessage = errorDesc;
            }
        } else {
            finalMessage = localError.toString();
            finalCallback = () => { location.reload() };
        }
        dialog.open(MessageDialog, {
            data: {
                message: finalMessage,
                callback: finalCallback
            }
        })
        console.log(error);
    }
}
这是一个对话框:

import { Component, Inject } from '@angular/core'

import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'

@Component({
    selector: 'message-dialog',
    template: `
        <p mat-dialog-title color="primary" class="centered">Attenzione</p>
        <div mat-dialog-content>{{message}}</div>
        <div mat-dialog-actions style="display: flex; justify-content: center">
            <button mat-button mat-dialog-close (click)="handleClick()">
                OK
            </button>
        </div>
    `,
    styleUrls: ['../common/style.css']
})
export class MessageDialog {

    message: string = "cose";
    callback: () => void;

    constructor( @Inject(MAT_DIALOG_DATA) private data,
        private diagref: MatDialogRef<MessageDialog>) {
        if (data) {
            this.message = data.message;
            this.callback = data.callback;
        }
    }

    handleClick() {
        this.diagref.close();
        if (this.callback) {
            this.callback();
        }
    }
}

在这个场景中,
handleClick
进行得很顺利,而
throwError
挂在那里。如果有人知道一个解决方案,请帮忙。

似乎在解决后我还没有回答自己。原来这是一个角度的错误,但不是你可能认为的错误。实际上,想要的行为是“非关闭”行为,这实际上是UI不更新

原因是错误发生后,错误处理程序在NgZone之外执行,这意味着angular不知道您所做的UI更改。(我不得不用谷歌zone.js来了解这一切。)

解决方案是再次注入NgZone并在区域内手动运行stuff。它可以工作,但由于区域内的错误被发送回处理程序,因此可能会导致无限循环(错误、显示错误的操作导致新错误、新错误触发再次抛出错误的相同操作等等)。为了避免这种情况,我做了一件事,就是设置了一个防护装置,使其不会向UI显示多个错误,这样,除非用户告诉我可以,否则任何后续错误都只会发送到控制台。以下是工作代码:

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

import { Router } from '@angular/router'

import { MatDialog } from '@angular/material'


import { HttpErrorResponse } from '@angular/common/http'


import { MessageDialog } from '../dialogs/message.dialog'

import { LoginService } from '../login/main.service'



@Injectable()

export class DialogErrorHandler extends ErrorHandler {


    private elaborating: boolean = false;


    constructor(private injector: Injector, private ngzone: NgZone) {

        super();

    }


    handleError(error: any): void {

        if (!this.elaborating) {

            this.elaborating = true;

            let localError = error;

            let finalMessage: string = "Errore sconosciuto";

            let finalCallback: () => void = () => { console.log("default callback") };


            let dialog: MatDialog = this.injector.get(MatDialog);

            let login: LoginService = this.injector.get(LoginService);

            let router: Router = this.injector.get(Router);

            // nessun dialog per TypeError, evita problemi con MatSelect

if (localError instanceof TypeError) {

                this.elaborating = false;

            } else {

                if (localError instanceof HttpErrorResponse && localError.error instanceof Error) {

                    localError = localError.error

                }

                if (localError instanceof HttpErrorResponse) {

                    let errorDesc = 'Request to ' + localError.url + "\n" + localError.status +

                        " " + localError.statusText + ": " + localError.error;

                    switch (localError.status) {

                        case 403:

                            finalMessage = "La sessione è scaduta, ripeti il login.";

                            finalCallback = () => { login.logout(); router.navigate['/login']; };

                            break;

                        case 500:

                            finalMessage = "Errore sul server - " + errorDesc;

                            break;

                        default:

                            finalMessage = errorDesc;

                    }

                } else {

                    finalMessage = localError.message;

                }

                this.ngzone.run(() => {

                    dialog.open(MessageDialog, {

                        data: {

                            message: finalMessage,

                            callback: () => { finalCallback(); this.elaborating = false; }

                        }

                    })

                })

            }

        }

        super.handleError(error);

    }

}

我将NgZone注入到处理程序中,并使用它显示对话框,我有一个精巧的布尔值,可以避免无限递归。

我从errorhandler调用的服务中打开错误对话框。在错误处理程序中,我只需要在调用服务方法this.ngzone.run(()=>{this.service.openErrorDialog('message');})的地方添加这一行;我没有super.handleError(错误);处理程序结束。这样,我现在可以关闭错误对话框,因为对话框位于角度区域内。我的问题是,单击事件只发生在我单击按钮,然后单击其他地方之后。第二次单击后,对话框关闭。
import { ErrorHandler, Injectable, Injector, NgZone } from '@angular/core'

import { Router } from '@angular/router'

import { MatDialog } from '@angular/material'


import { HttpErrorResponse } from '@angular/common/http'


import { MessageDialog } from '../dialogs/message.dialog'

import { LoginService } from '../login/main.service'



@Injectable()

export class DialogErrorHandler extends ErrorHandler {


    private elaborating: boolean = false;


    constructor(private injector: Injector, private ngzone: NgZone) {

        super();

    }


    handleError(error: any): void {

        if (!this.elaborating) {

            this.elaborating = true;

            let localError = error;

            let finalMessage: string = "Errore sconosciuto";

            let finalCallback: () => void = () => { console.log("default callback") };


            let dialog: MatDialog = this.injector.get(MatDialog);

            let login: LoginService = this.injector.get(LoginService);

            let router: Router = this.injector.get(Router);

            // nessun dialog per TypeError, evita problemi con MatSelect

if (localError instanceof TypeError) {

                this.elaborating = false;

            } else {

                if (localError instanceof HttpErrorResponse && localError.error instanceof Error) {

                    localError = localError.error

                }

                if (localError instanceof HttpErrorResponse) {

                    let errorDesc = 'Request to ' + localError.url + "\n" + localError.status +

                        " " + localError.statusText + ": " + localError.error;

                    switch (localError.status) {

                        case 403:

                            finalMessage = "La sessione è scaduta, ripeti il login.";

                            finalCallback = () => { login.logout(); router.navigate['/login']; };

                            break;

                        case 500:

                            finalMessage = "Errore sul server - " + errorDesc;

                            break;

                        default:

                            finalMessage = errorDesc;

                    }

                } else {

                    finalMessage = localError.message;

                }

                this.ngzone.run(() => {

                    dialog.open(MessageDialog, {

                        data: {

                            message: finalMessage,

                            callback: () => { finalCallback(); this.elaborating = false; }

                        }

                    })

                })

            }

        }

        super.handleError(error);

    }

}