Arnold Weekley
Arnold Weekley

Reputation: 11

Angular Error Dialog invoked from ErrorHandler doesn't display data on TypeError

I've gotten most of this example working thanks to other posts, but I'm still having one problem.

If I throw an error, the Error Dialog works as expected. The Error Dialog also works if I throw an HTTP error (including if I catch the error in pipe and return throwError(error)). However, if a typescript error as thrown if attempting to display an undefined object, the Error Dialog doesn't display data from the component, and the dialog doesn't close.

Here's the code I'm experimenting with.


    import { BrowserModule } from '@angular/platform-browser';
    import { Component, Inject, Injectable, OnInit, ErrorHandler, NgModule, NgZone } from '@angular/core';
    
    import { HttpClientModule } from '@angular/common/http';
    import { HttpClient } from '@angular/common/http';
    
    import { Observable } from 'rxjs';
    
    import { MatDialogModule } from '@angular/material/dialog';
    import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
    import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
    
    @Component({
      selector: 'app-root',
      template: `
    <div class="content">
    <h1>{{title}}</h1>
    <h3>{{something[0]['text']}}</h3>
    <button (click)="forceFakeError()">Force Fake Error</button>
    <br>
    <button (click)="forceHttpError()">Force HTTP Error</button>
    <br>
    <button (click)="forceTypeError()">Force Type Error</button>
    </div>
    `,
      styles: ['div.content {padding: 25px;}', 'button {margin: 10px}']
    })
    export class AppComponent {
      title = 'dialog-issue';
      something = [{ text: 'This is some test text' }];
      data: any;
    
      constructor(public dialog: MatDialog, private http: HttpClient) {}
    
      forceFakeError() {
          throw(new Error('Fake  eror...'));
      }
    
      forceHttpError() {
        this.http.get<any>('https://garbageinout.io').subscribe({
            next: data => {
                this.data = data;
            },
            error: error => {
                console.error('There was an error!', error);
                throw(error);
            }
        });
      }
    
      forceTypeError() {
        this.something.length = 0;
      }
    }
    
    
    @Injectable({
        providedIn: 'root'
    })
    export class ErrorHandlerService implements ErrorHandler {
    
       handling = false;
    
        constructor(private dialog: MatDialog, private ngZone: NgZone) {}
    
        handleError(error: any) {
            if (this.handling) {
                console.log('errorhandler already handling error message: ' + error.message);
                return;
            }
            this.handling = true;
            console.log('errorhandler error: ', error);
            console.log('errorhandler error name: ' + error.constructor.name);
            const errMsg = error.message ?? 'No message found!';
            console.log('errorhandler error message: ' + error.message);
            this.ngZone.run(() => {
                this.displayErrorMessage('Application Error', errMsg)
                    .subscribe((resp) => {
                        console.log('Response is: ', resp);
                        this.handling = false;
                    });
          });
        }
    
        displayErrorMessage(title: string, msg: string): Observable<string> {
            const dialogRef = this.dialog.open(TestDialog, 
                {width: '400px', disableClose: true, data: { title: title, message: msg}
            });
            return dialogRef.afterClosed();
        }
    }
    
    
    export interface ErrorData {
       title: string;
       message: string;
    }
    
    @Component({
      selector: 'app-error',
      template: `
    <div>
    <h1 mat-dialog-title>{{title}}</h1>
    <div mat-dialog-content>
      <p>Click OK to continue, or click on the "Click here"
         link to restart the app.</p>
      <p>Message: {{msg}}</p>
      <p>{{dateTime()}}</p>
      <p><a href="/index">Click here</a> to restart app</p>
    </div>
    <button mat-raised-button (click)="onClick()">OK</button>
    </div>
    `,
      styles: ['button { margin: 5 }']
    })
    export class TestDialog implements OnInit {
        title: string = 'Fake Title';
        msg: string = 'Fake string';
    
        constructor(public dialogRef: MatDialogRef<TestDialog>,
                @Inject(MAT_DIALOG_DATA) public data: ErrorData) {
            this.title = data.title;
            this.msg = data.message ?? 'Message not provided...';
        }
    
        ngOnInit() {
            console.log('error dialog, title: ', this.title, ', msg: ', this.msg);
        }
    
        dateTime(): string {
            return new Date().toString();
        }
    
        onClick() {
            console.log('entering error dialog onClick event');
            this.dialogRef.close('OK!');
        }
    }
    
    
    @NgModule({
      declarations: [
        AppComponent,
        TestDialog
      ],
      imports: [
        BrowserModule,
        BrowserAnimationsModule,
        MatDialogModule
      ],
      exports: [
        HttpClientModule,
        MatDialogModule
      ],
      providers: [
          { provide: ErrorHandler, useClass: ErrorHandlerService }
      ],
      bootstrap: [AppComponent]
    })
    export class AppModule { }

My environment is:


    Angular CLI: 13.2.2
    Node: 16.13.0
    Package Manager: npm 8.4.1
    OS: darwin arm64
    
    Angular: 13.2.1
    ... animations, cdk, common, compiler, compiler-cli, core, forms
    ... material, platform-browser, platform-browser-dynamic, router
    
    Package                         Version
    ---------------------------------------------------------
    @angular-devkit/architect       0.1302.2
    @angular-devkit/build-angular   13.2.2
    @angular-devkit/core            13.2.2
    @angular-devkit/schematics      13.2.2
    @angular/cli                    13.2.2
    @schematics/angular             13.2.2
    rxjs                            7.5.2
    typescript                      4.5.5

and I'm using the command "ng serve --ssl --configuration production" for execution.

Thank you in advance for any insight on how to solve this problem.

Upvotes: 1

Views: 495

Answers (1)

christoph
christoph

Reputation: 1051

That is because no Error was raised. Your something is an array and has a length property. Sure it should be readonly but javascript executes it no matter what - although it has no effect on the value of the length property.

You can test it directly in your browsers dev console. Just type and enter:

[].length = 0;

No error is raised.

I suppose your handler whould correctly handle something like this:

this.something.unknownProperty.anotherProperty = 0;

As unknownProperty will return undefined, javascript can´t execute that statement. It can´t resolve anotherProperty from undefined. That raises an error.

Upvotes: -1

Related Questions