2017-10-18 12 views
2

내가받은 오류에 대한 정보가있는 대화 상자를 표시하기 위해 Angular4 응용 프로그램의 일반 ErrorHandler를 구현하려고했습니다. https://plnkr.co/edit/dZ9yNf?p=previewMaterial Design을 사용한 Angular2 응용 프로그램의 일반 오류 처리기

이제 문제 : 이것은 내가 올 것을 가진 plunkr가

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(); 
     } 
    } 
} 

이 대화하는 동안

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); 
    } 
} 

:

오류 핸들러 오류가 발생하면 MatDialog I에 메시지가 표시되지 않고 닫히지 않습니다. 그것은 올바르게 콜백 함수를 실행하지만, 거기에 머물러 있습니다.

  • plunkr에서 동작을 재현하지 못했습니다. 그 이유는 모르겠지만 plunkr이 내 HttpErrorResponse 객체를 다른 오류로 래핑한다는 사실과 관련이 있다고 생각합니다. 그래도 내 개발 환경에서 오류 유형을 고려하는 모든 코드에 주석을 달더라도 오류가 있습니다.
  • 매트 스니커드 (MatSnackBar)를 MatSnackBar (행운, 여전히 같은 문제)로 대체 해 보았습니다. 스낵바가 나타나고 사라지는 것을 원하지 않습니다.
  • 대화 자체는 다른 상황에서도 사용되며 정상적으로 닫히거나 아무런 문제가없는 상황에서는 대화 상자 자체가 사용됩니다.
  • 디버깅 문제는 어떻게 든 대화 상자를 닫으려고 시도하지만 MatDialogContainer에서 상태 변경 이벤트가 발생하지 않는다는 것을 알았습니다. 실행 후 상태 변경이 표시되지만 콜백되지는 않습니다.

내가 뭘 잘못하고 있니? 왜이 이상한 행동? 조언 있니?

P. 주석 처리 된 항목은 필요한 항목이지만 문제를 해결하지 않고 처리기를 단순화하기 위해 제거 할 수있었습니다. 거기에 주석 처리가되어 있지만 문제는 아닙니다.

편집 : 9 일 후, 텀블 위드 배지 등의 오류가 발생하여 재현 할 수있었습니다. 같은 plunkr, 차이는 오류의 출처에 있습니다. 특히 : throwError가 달려있는 동안이 시나리오에서는

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가 원활하게 이동합니다. 아무도 해결책을 안다면 도움을주십시오.

답변

1

나는 그것을 해결하고 나 자신을 대답하지 않은 것 같습니다. 그것이 당신이 생각할지도 모르는 버그가 아니라, here the issue I opened이라는 버그였습니다. 실제로 원하는 동작은 "닫히지 않는"것입니다. 실제로는 업데이트하지 않는 UI입니다.

오류가 발생하면 오류 처리기가 NgZone 외부에서 실행되므로 각도 변경으로 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를 주입하고 대화 상자를 표시하는 데 사용하고, 나는 무한 재귀를 방지해야 정교하게 부울이 다음은 작업 코드입니다.