【问题标题】:General error handler for Angular2 application using Material Design使用 Material Design 的 Angular2 应用程序的一般错误处理程序
【发布时间】:2017-10-18 13:06:25
【问题描述】:

我试图为我的 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();
        }
    }
}

这是我遇到的问题:https://plnkr.co/edit/dZ9yNf?p=preview

现在,问题是在我启动错误后,我显示的 MatDialog 不显示消息并且不关闭。它正确地执行了回调函数,但它停留在那里。

  • 我无法重现 plunkr 中的行为。我不知道为什么,但我怀疑这与 plunkr 将我的 HttpErrorResponse 对象包装在另一个错误中这一事实有关。尽管如此,在我的开发环境中,即使我注释了所有考虑错误类型的代码,错误仍然存​​在。
  • 我试过用 MatSnackBar 代替 MatDialog,不走运,还是同样的问题,snackbar 出现并且不想消失。
  • 对话框本身也用于其他情况,在这些情况下它会正常关闭,不会产生任何问题。
  • 调试我发现问题在于对话框以某种方式尝试自行关闭,但从未发出 MatDialogContainer 上的状态更改事件,因为在执行之后我看到状态更改但没有调用回调。

我做错了什么?为什么会有这种奇怪的行为?你有什么建议吗?

附:评论的东西是我需要的东西,但我已经能够在不解决问题的情况下取出来简化处理程序。它在那里,不会被注释,但这不是问题。

编辑:经过 9 天,风滚草徽章和其他东西,我已经能够重现错误。同样的 plunkr,不同之处在于错误的来源。特别是:

handleClick(){
    Observable.throw(new Error("local error")).subscribe(
      () => {},
      (err) => {throw err)
    )
  }

  throwError(){
    this.http.get("http://www.google.com/thisshouldnotexist").subscribe(
      () => {},
      (err) => {throw err}
    )
  }

在这种情况下,handleClick 顺利进行,而throwError 则挂在那里。如果有人知道解决方案,请帮助。

【问题讨论】:

    标签: angular typescript error-handling dialog material-design


    【解决方案1】:

    好像解决后我还没有回答自己。原来这是一个 angular here the issue I opened 的错误,但不是您可能认为的错误。实际上,想要的行为是“非关闭”行为,实际上是 UI 没有更新。

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

    解决方案是再次注入 NgZone 并在区域内手动运行东西。它可以工作,但是由于区域内的错误被发送回处理程序,它可能会导致无限循环(错误,显示错误的操作会导致新错误,新错误触发相同的操作再次引发错误等等...)。我为避免这种情况所做的就是设置一个保护措施,不要向 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 并使用它来显示对话框,并且我有应该避免无限递归的详细布尔值。

    【讨论】:

    • 我从错误处理程序调用的服务中打开错误对话框。在错误处理程序中,我只需要在调用服务方法 this.ngzone.run(() => { this.service.openErrorDialog('message');}); 的地方添加这一行而且我没有 super.handleError(error);处理程序结束。这样我现在可以关闭错误对话框,因为对话框在角度区域内。我的问题是单击事件仅在我单击按钮然后单击其他位置后才发生。第二次点击后对话框关闭。
    猜你喜欢
    • 2017-01-21
    • 2016-12-12
    • 2014-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多