So far, I've successfully done the following:
✓ HMR (Hot Module Reload) set up
✓ Angular (5) and Material working well
✓ Open a dialog (code snippet below)
// ...
constructor(private dialog: MatDialog){}
//...
public openDialog(){
this.dialogRef = this.dialog.open(someDialogComponent, {
width: '300px'
});
}
✓ Make a change on dialog or on dialog's parent controller
✓ HMR is triggered (yay)
✖ Dialog hangs in dead state, page is essentially frozen due to the dialog and backdrop being "stuck" and unclickable
I have tried to hook into ngOnInit
and ngOnDestroy
in the parent or dialog controller to close the dialog reference if one exists, I've also tried to dialog.closeAll()
, but this has not worked. Also, ideally the dialog wouldn't have to close, but I can't seem to fix this zombie dialog issue.
Has anyone encountered this?
MatDialog creates modal dialogs that implements the ARIA role="dialog" pattern by default. You can change the dialog's role to alertdialog via MatDialogConfig . You should provide an accessible label to this root dialog element by setting the ariaLabel or ariaLabelledBy properties of MatDialogConfig .
To close a mat dialog from its component. ts file or programmatically, we have to inject the MatDialogRef<DialogComponentName> in its constructor. The MatDialogRef has a built-in method close() , which you can call anytime whenever you want to close the mat dialog box.
In the dialog component, we need to create an instance of 'MatDialogRef' which we should import from '@angular/material/dialog'. Import 'MatDialogModule' from '@angular/material' in app. module. ts file.
You can pass any kind of data to the component by adding data to the MatDialog's open() options. }, }); You can retrieve the data by injecting MAT_DIALOG_DATA in the opened component.
I've been struggling with this and found a less than ideal solution to get by for now. It removes any angular dialogs from the DOM, during the hmr destroy event.
const elements = document.getElementsByClassName('cdk-overlay-container');
for (let i = 0; i < elements.length; i++) {
elements[i].innerHTML = '';
}
Then in the OnInit, we re-create all the dialogs that were open, passing in their data. The only problem is that this solution retains old dialog instances, so they are not dismiss-able from a background click. However, if the dialog has a close button wired up on it, then it will dismiss properly. The OpenDialogs property in StateService could probably be changed to a TemplateRef[] full of the componentInstances, this might solve the issue, but I'm not sure.
Anyway, a hack solution until official support for dialogs + hmr arrives.
app.module.ts
export class AppModule {
constructor(private state: StateService, public dialog: MatDialog) { }
OnInit(store) {
if (store !== undefined) {
this.state.SetState(store.State);
for (let i = 0; i < this.state.OpenDialogs.length; i++) {
const t = this.state.OpenDialogs[i].componentInstance;
this.dialog.open(t.constructor, { data: t.data });
}
}
}
OnDestroy(store) {
this.state.OpenDialogs = this.dialog.openDialogs;
store.State = this.state;
const elements = document.getElementsByClassName('cdk-overlay-container');
for (let i = 0; i < elements.length; i++) {
elements[i].innerHTML = '';
}
}
}
state.service.ts:
import { Injectable } from '@angular/core';
import { MatDialogRef } from '@angular/material';
@Injectable({
providedIn: 'root'
})
export class StateService {
public OpenDialogs: MatDialogRef<any>[];
constructor() {
}
public SetState(_state: StateService) {
this.OpenDialogs = _state.OpenDialogs;
}
}
main.ts
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
// tslint:disable-next-line:no-shadowed-variable
function bootstrap(AppModule) {
return platformBrowserDynamic().bootstrapModule(AppModule)
.then(moduleRef => {
if (environment.hmr) {
if (module['hot']) {
module['hot']['accept']();
if (moduleRef.instance['OnInit']) {
if (module['hot']['data']) {
moduleRef.instance['OnInit'](module['hot']['data']);
}
}
if (moduleRef.instance['OnStatus']) {
module['hot']['apply']((status) => {
moduleRef.instance['OnStatus'](status);
});
}
if (moduleRef.instance['OnCheck']) {
module['hot']['check']((err, outdatedModules) => {
moduleRef.instance['OnCheck'](err, outdatedModules);
});
}
if (moduleRef.instance['OnDecline']) {
module['hot']['decline']((dependencies) => {
moduleRef.instance['OnDecline'](dependencies);
});
}
module['hot']['dispose'](store => {
if (moduleRef.instance['OnDestroy']) {
moduleRef.instance['OnDestroy'](store);
}
moduleRef.destroy();
if (moduleRef.instance['AfterDestroy']) {
moduleRef.instance['AfterDestroy'](store);
}
});
}
}
return moduleRef;
});
}
bootstrap(AppModule);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With