ng-bootstrap / ng-bootstrap

Angular powered Bootstrap
https://ng-bootstrap.github.io
MIT License
8.22k stars 1.55k forks source link

NgbModal open not working from within ErrorHandler #3860

Open ihordeyneka opened 4 years ago

ihordeyneka commented 4 years ago

When creating an instance of NgbModal with injector from within custom ErrorHandler the modal is not displayed properly when error happens as part of HTTP request (for example HTTP ERROR 500). It works fine when you just throw an exception with throw new Error();

Error handler method:

  handleError(error: Error) {
    console.log(error);

    this.modalService = this.injector.get(NgbModal);
    this.modalService.open('ERROR HAPPENED', {});
  }

Error handler registration:

  providers: [
    { provide: ErrorHandler, useClass: AppErrorHandler }
  ]

HTTP error simulation:

  open() {
    console.log('begin');
    this.http.get<any>('http://www.test.te').subscribe(() => {
      console.log('end');
    });
  }

Here's the StackBlitz example: https://angular-saggje.stackblitz.io

When you click TRIGGER ERROR button for the first time, it generates ngb-modal-window component in the HTML, but it doesn't have all of the styles and so not visible properly. When you click the button for the second time, you can see the first modal and then the second modal when the first is closed.

Angular: 9.1.1

ng-bootstrap: 7.0.0

Bootstrap: 4

rmlira commented 4 years ago

Same problem here.

diveshdobal commented 3 years ago

I face same issue. did you found solution for it?

crazedmodder commented 3 years ago

Alright so unfortunately for the OP they have probably moved on already, but for other people who find this through Google, or the other people in here, I have found what could be the solution on Stack Overflow.

Apparently the handleError method is run by Angular outside of zone, which probably causes issues with change detection, etc. The solution is to change the app-error-handler.ts file in the OP's StackBlitz to:

import { ErrorHandler, Injector, Injectable, NgZone } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { HttpErrorResponse } from '@angular/common/http';

@Injectable()
export class AppErrorHandler implements ErrorHandler {
  constructor(private injector: Injector) {}

  handleError(error: Error) {
    console.log(error);

    var modalService = this.injector.get(NgbModal);
    var zone = this.injector.get(NgZone);
    zone.run(() => modalService.open('ERROR HAPPENED', {}));
  }
}

Here's the link for the solution on Stack Overflow: https://stackoverflow.com/a/48196506/1993631

Also, I figure this is not an issue with ng-bootstrap itself, and more about how Angular runs things outside of Zone. This scenario probably just needs to be documented more (I didn't really see any reference to this on any of the examples I saw of using modals on error, I couldn't even reproduce the issue myself on StackBlitz, not sure how OP did it with basically the same code).