使用Material Design

时间:2017-10-18 13:06:25

标签: 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 { 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 I显示屏没有显示消息而且没有关闭。它正确执行回调函数,但它仍然存在。

  • 我无法重现plunkr中的行为。我不知道为什么,但我怀疑它与plunkr在另一个错误中包装我的HttpErrorResponse对象这一事实有关。尽管如此,在我的开发环境中,即使我评论所有考虑错误类型的代码,也会出现错误。
  • 我尝试用MatSnackBar替换MatDialog,没有运气,仍然存在同样的问题,小吃栏出现并且不想消失。
  • 对话框本身也用于其他情况,在这种情况下它会正常关闭,不会出现任何问题。
  • 调试我发现问题在于,对话框试图关闭自己,但是MatdialogContainer上的状态更改事件从未发出,因为在执行之后我看到状态发生了变化但没有调用回调。 / LI>

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

P.S。评论的东西是我需要的东西,但我已经能够解决简化处理程序而不解决问题。它在那里,将被取消注释,但这不是问题。

编辑: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挂起。如果有人知道解决方案,请帮助。

1 个答案:

答案 0 :(得分:2)

似乎解决后我没有回答自己。事实证明这是一个角度here the issue I opened的错误,但不是你可能想到的错误。实际上,想要的行为是“非关闭”行为,实际上是UI没有更新。

原因是在出错之后,错误处理程序在NgZone外部执行,这意味着angular将不知道您所做的UI更改。 (我必须google 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并使用它来显示对话框,并且我有一个应该避免无限递归的详细布尔值。