使用材料设计的Angular2应用程序的通用错误处理程序
我试图为Angular4应用程序实现一个通用的ErrorHandler,目的是显示一个对话框,其中包含有关我收到的错误的一些信息 这是错误处理程序:使用材料设计的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
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);
}
}