Ignacio Soler Garcia
Ignacio Soler Garcia

Reputation: 21855

Modal called from service is not shown as a modal but as page content in angular

I'm building a global error handler so it shows a bootstrap modal dialog showing details about the error. I have the following setup:

enter image description here

The error modal component defines a method called open(), its template includes a button (to debug it) that calls the open() and everything works fine, the modal is correctly shown with the binded text.

ErrorModalComponent code:

import { Component, ViewChild, TemplateRef } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';

import { GlobalErrorHandler } from './global-error-handler.service';

@Component({
  selector: 'app-error-modal',
  templateUrl: './error-modal.component.html',
  styles: []
})
export class ErrorModalComponent {

  private _message: string;

  @ViewChild('content') _modalTemplate: TemplateRef<any>;

  constructor(private _modalService: NgbModal, private _globalErrorHandler: GlobalErrorHandler) {
    this._globalErrorHandler.errorEventSource$.subscribe(errorMessage => {
      this._message = errorMessage.toString();
      this.open();
    });
  }

  open() {
    this._modalService.open(this._modalTemplate);
    this._message = "A change test";
  }

  public get message(): string {
    return this._message;
  }
}

ErrorModalComponent html:

<button class="btn btn-info" (click)="open()">Show Modal</button>
<ng-template #content let-c="close" let-d="dismiss">
  <div class="modal-content">
    <div class="modal-header">
      <h4 class="modal-title">It's not you, it's me: unhandled error</h4>
      <button type="button" class="close" aria-label="Close" (click)="d('Cross click')" data-dismiss="modal">
        <span aria-hidden="true">&times;</span>
      </button>
    </div>
    <div class="modal-body">
      <p>We are sorry but something has slipped by and we have an uncontrolled error.</p>
      <p>A notification has been sent to us and we will process it soon.</p>
      <p>{{message}}</p>
    </div>
    <div class="modal-footer">
      <div class="col-4">
        <button type="button" class="btn btn-danger btn-block" (click)="c('Close click')" data-dismiss="modal">Close</button>
      </div>
    </div>
  </div>
</ng-template>

GlobalErrorHandler code:

import { ErrorHandler,  Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class GlobalErrorHandler implements ErrorHandler {

  private errorEventSource = new Subject<string>();
  errorEventSource$ = this.errorEventSource.asObservable();

  handleError(error: any) {
    this.errorEventSource.next(error);
  }
 }

When I put an exception somewhere in the code the event is captured, the open method called but the modal is not correctly shown and the binded text does not appear. It appears at the bottom of the page and it is disabled and you cannot click anywhere.

I don't get why this difference when calling the same method from two different places. Any hint?

This is the modal after clicking the button:

enter image description here

And this is the modal after raising an exception:

enter image description here

Upvotes: 2

Views: 875

Answers (1)

Mattias K
Mattias K

Reputation: 65

I had the exact same problem but with a slightly different setup. I had a globalErrorHandler with a "handleError" function. apparently that function executes outside of the angular zone.

I fixed this by running the ngbMdal in zone.runguarded.

this.zone.runGuarded(() => {
        this.modalService = this.injector.get(NgbModal);
        const modalRef = this.modalService.open(ErrorComponent);
        modalRef.componentInstance.errorMessage = error.message;
        modalRef.componentInstance.stack = error.stack;
    });

Don't forget the injection in the constructor

private zone: NgZone

Upvotes: 1

Related Questions