Components: Bug MatDialog is unclosable

Created on 30 Jan 2018  路  17Comments  路  Source: angular/components

Bug, feature request, or proposal:

Bug

What is the expected behavior?

Should be able to close dialog

What is the current behavior?

Cannot close dialog - for a predictable/certain code path

What are the steps to reproduce?

Here is the problem:
https://www.useloom.com/share/7eb9c523ee3144fb94bb2b49b57dee1b

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

Angular5, Material2, MacOS, tsc version 2.6.1

More info

in both cases, the dialog is created using the same code, here is the code to create dialog:

'use strict';

import {Component, OnInit, Inject, Injectable} from '@angular/core';
import {MainDataService} from '../../services/main';
import {MAT_DIALOG_DATA, MatDialogRef, MatDialogConfig} from '@angular/material';
import {MatDialog} from '@angular/material/dialog';

@Component({
  templateUrl: './stop-recording-dialog.component.html',
  styleUrls: ['./stop-recording-dialog.component.scss']
})

export class StopRecordingDialogComponent implements OnInit {

  public dialogResult: any;
  escapeToClose = true;

  constructor(public dialogRef: MatDialogRef<StopRecordingDialogComponent>,
              @Inject(MAT_DIALOG_DATA) public data: string,
              private mds: MainDataService) {
  }

  onClickClose() {
    this.dialogRef.close();
    this.mds.dialogEmitter.emit({
      name: 'stop-recording-run-dialog-closed',
      value: 'x'
    });
  }

}


@Injectable()
export class StopRecordingDialog {

  escapeToClose = true;
  dialogRef: MatDialogRef<StopRecordingDialogComponent> | null;

  config = Object.assign(new MatDialogConfig(), {
    width: '600px',
    data: 'This text is passed into the dialog!'
  });

  constructor(public dialog: MatDialog, private mds: MainDataService) {}

  openDialog() {

    this.dialogRef = this.dialog.open(StopRecordingDialogComponent, this.config);

    this.dialogRef.beforeClose().subscribe((result: string) => {
      console.log(`Dialog before closed: ${result}`);
    });

    this.dialogRef.afterClosed().subscribe(result => {
      console.log(`Dialog after closed: ${result}`);
    });

  }
}

even when it fails to close, the onClickClose() method is called, so not sure why the dialog won't close.

Most helpful comment

Closing since this does sound like an issue with escaping the Angular zone. You can re-enter the zone by injecting NgZone and doing

ngZone.run(() => {
   dialog.close();
});

All 17 comments

Any special reason to call the close() method before emitting from this.mds.dialogEmitter?

Yeah, so close() closes the dialog ( in theory). The emitter is used to broadcast which option/action the user clicked in the dialog

Would it change anything if you called close() after this.mds.dialogEmitter (just switch the order you are calling them)?

This is almost certainly an NgZone issue, which probably stems from using the browser extension callback. See https://github.com/angular/material2/issues/7550

@julianobrasil @willshowell yeah I will try calling close() in the next tick of the event loop which will put it after the this.mds.dialogEmitter call...it's just weird that it works always in one case, yet fails always in another case.

nahh..so even if I do this:

 onClickClose() {
    console.log('you have closed the dialog.');
    const self = this;
    setTimeout(function(){
      self.dialogRef.close();
    }, 50);
    this.mds.dialogEmitter.emit({
      name: 'stop-recording-run-dialog-closed',
      value: 'x'
    });
  }

that should put it on the next tick, and that didn't work, let me try just adding it on the last line, instead of in the next tick

@ORESoftware did you make sure you are opening your dialog within the NgZone?

@willshowell, no, how do I do that?

I assume that if it's in the same tick, it will most likely be in the same zone, and note that this fails as well:

  onClickClose() {
    console.log('you have closed the dialog.');
    this.mds.dialogEmitter.emit({
      name: 'stop-recording-run-dialog-closed',
      value: 'x'
    });
    this.dialogRef.close();   // put this after the above call, still does not work
  }

I am very familiar with Node.js domains, and I heard that NgZones were similar in nature

Ok - you don't have to try it, but this exact issue has come up a half-dozen times, and it is always because they opened the dialog outside of the zone.

@willshowell I got it to work with the ngZone code you mentioned. TBH, that was fairly hardcore lol.
I basically replaced this:

   this.stopRecordingSubject = this.mds.stopRecordingSubject.take(1).subscribe(v => {
      setTimeout(() => {
        this.stopRecordingDialog.openDialog();
       }, 50);
    });

with this:

   this.stopRecordingSubject = this.mds.stopRecordingSubject.take(1).subscribe(v => {
      this.ngZone.run(() => {
        this.stopRecordingDialog.openDialog();
      });
    });

thanks for your help!

@willshowell so just like Angular1.x, is there a way to use setTimeout, so that the callback fn, is called within the same zone? Is there something we can inject like with Angular 1.x $timeout? You know what I am saying right?

@ORESoftware I actually don't have any Angular 1 experience, so I dunno. The root of this issue is that your browser extension callback is not executing within the NgZone. You can probably pull the ngZone.run() part into this.mds so that it's consolidated better and not left to your dialog service to handle.

It's a common issue when incorporating 3rd party libs/apis.

Closing since this does sound like an issue with escaping the Angular zone. You can re-enter the zone by injecting NgZone and doing

ngZone.run(() => {
   dialog.close();
});

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

_This action has been performed automatically by a bot._

Was this page helpful?
0 / 5 - 0 ratings

Related issues

LoganDupont picture LoganDupont  路  3Comments

vanor89 picture vanor89  路  3Comments

constantinlucian picture constantinlucian  路  3Comments

michaelb-01 picture michaelb-01  路  3Comments

RoxKilly picture RoxKilly  路  3Comments