I'm submitting a ... (check one with "x")
[x] bug report => Search github for a similar issue or PR before submitting
[ ] feature request => Please check if request is not on the roadmap already https://github.com/primefaces/primeng/wiki/Roadmap
[ ] support request => Please do not submit support request here, instead see http://forum.primefaces.org/viewforum.php?f=35
Plunkr Case (Bug Reports)
Please demonstrate your case at stackblitz by using the issue template below. Issues without a test case have much less possibility to be reviewd in detail and assisted.
https://stackblitz.com/github/primefaces/primeng-issue-template
Current behavior
When two dynamic dialogs are open with the same component, the first one (the one under the second one) cannot be closed progammatically.
Expected behavior
All dialogs should be allowed to be closed programmatically.
Minimal reproduction of the problem with instructions
What is the motivation / use case for changing the behavior?
Two dynamic dialogs created with the same component should be programmatically closable.
Please tell us about your environment:
Angular version: 9.1.3
PrimeNG version: 9.0.6
Browser: all
Language: all
This is related to the closed issue #8547
The app URL of the stackblitz: https://angular-primeng-multiple-dynamic-dialog-close-bug.stackblitz.io
The editor URL of the stackblitz: https://stackblitz.com/edit/angular-primeng-multiple-dynamic-dialog-close-bug
Don't pay too much attention to the styling ;-)
I found the code that is responsible for this behavior.
In the DialogService class only the most recent created dialog is saved in the dialogComponentRef property. In the function removeDialogComponentFromBody the dialog saved in the dialogComponentRef is closed. Other dialogs trying to close will then also call the same removeDialogComponentFromBody function but the dialogComponentRef already references another dialog object.
Here is my suggestion to allow closing multiple opened DynamicDialogs:
Replace the dialogComponentRef with a map object where the keys are the DynamicDialogRefs and the values are the ComponentRefs created in the appendDialogComponentToBody function. And when closing one dialog the function removeDialogComponentFromBody needs the DialogRef as an additional argument with which you can get the ComponentRef from the map object.
Suggested changes to DialogService class:
@Injectable()
export class DialogService {
dialogComponentRefMap: Map<DynamicDialogRef, ComponentRef<DynamicDialogComponent>> = new Map();
constructor(private componentFactoryResolver: ComponentFactoryResolver, private appRef: ApplicationRef, private injector: Injector) { }
public open(componentType: Type<any>, config: DynamicDialogConfig) {
const dialogRef = this.appendDialogComponentToBody(config);
this.dialogComponentRefMap.get(dialogRef).instance.childComponentType = componentType;
return dialogRef;
}
private appendDialogComponentToBody(config: DynamicDialogConfig) {
const map = new WeakMap();
map.set(DynamicDialogConfig, config);
const dialogRef = new DynamicDialogRef();
map.set(DynamicDialogRef, dialogRef);
const sub = dialogRef.onClose.subscribe(() => {
this.dialogComponentRef.instance.close();
});
const destroySub = dialogRef.onDestroy.subscribe(() => {
this.removeDialogComponentFromBody(dialogRef);
destroySub.unsubscribe();
sub.unsubscribe();
});
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(DynamicDialogComponent);
const componentRef = componentFactory.create(new DynamicDialogInjector(this.injector, map));
this.appRef.attachView(componentRef.hostView);
const domElem = (componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement;
document.body.appendChild(domElem);
this.dialogComponentRefMap.set(dialogRef, componentRef);
return dialogRef;
}
private removeDialogComponentFromBody(dialogRef: DynamicDialogRef) {
if(!dialogRef || !this.dialogComponentRefMap.has(dialogRef)) {
return;
}
const dialogComponentRef = this.dialogComponentRefMap.get(dialogRef);
this.appRef.detachView(dialogComponentRef.hostView);
dialogComponentRef.destroy();
this.dialogComponentRefMap.delete(dialogRef)
}
}
@mrdlink Just a small correction.
~this.dialogComponentRef.instance.close();~ this.dialogComponentRefMap.get(dialogRef).instance.close();
Have you created a pull request for this yet? I'd love to have this available soon.
Needing this right now
@cagataycivici
I also need this fixed as soon as possible. Thank you!
I just created a pull request for this fix: https://github.com/primefaces/primeng/pull/8977
@cagataycivici
any updates on this?
We're needing this right now, our application is buggy with this dynamic dialog current behavior :( @cagataycivici
Version 10 has already been released and still hasn't been fixed. So far, I'm using version 9.0.0-rc. 4.
Any news on this?
any updates? @cagataycivici
almost 5 months and nothing, really sad. To deal with this problem, we've forked the repo and applied the pull request mentioned above. It's not what we wanted, but it's what we had to do :(
@mikaelboff It's a bit unconvenient, but it's the quickest way of using fixes until they get released. You can always merge the master branch of the original repository onto your master branch to keep your fork up to date.
Plus the pull request has been recently tagged for versionn10.0.1. So I hope it will be released with the next minor version.
@mrdlink Yes, it's the only way until it gets released. Hope that it'll be released as soon as possible!!
Waiting on this release!!
Just put providers: [DialogService] in
@Component({
selector: 'app-mp-kreiranje-promjena-sablona',
templateUrl: './mp-kreiranje-promjena-sablona.component.html',
styleUrls: ['./mp-kreiranje-promjena-sablona.component.css'],
providers: [DialogService]
})
If modal A open modal B, and B open C. Put this in modal B
A solution is to push the references into an array.
@Component({
selector: 'foo-bar',
templateUrl: './foo.component.html',
styleUrls: ['./foo.component.scss'],
providers: [DialogService]
})
export class FooComponent {
// define a variable to push the references to
openDynamicDialogRefs: DynamicDialogRef[] = [];
// open a dialog somewhere in your component
this.openDynamicDialogRefs.push(this.dialogService.open(FirstDialogToOpen, {}));
// then lets say at some point in your code you want to open another dialog but close the one that is currently visible, just do this:
this.closeOpenDialogs();
this.openDynamicDialogRefs.push(this.dialogService.open(SecondDialogToOpen, {}));
// function that destroys all existing dialogs
closeOpenDialogs() {
this.openDynamicDialogRefs.forEach((openDynamicDialogRef: DynamicDialogRef) => openDynamicDialogRef.destroy());
this.openDynamicDialogRefs = [];
}
}
Most helpful comment
I found the code that is responsible for this behavior.
In the
DialogServiceclass only the most recent created dialog is saved in thedialogComponentRefproperty. In the functionremoveDialogComponentFromBodythe dialog saved in thedialogComponentRefis closed. Other dialogs trying to close will then also call the sameremoveDialogComponentFromBodyfunction but thedialogComponentRefalready references another dialog object.Here is my suggestion to allow closing multiple opened
DynamicDialogs:Replace the
dialogComponentRefwith a map object where the keys are theDynamicDialogRefs and the values are theComponentRefs created in theappendDialogComponentToBodyfunction. And when closing one dialog the functionremoveDialogComponentFromBodyneeds theDialogRefas an additional argument with which you can get theComponentReffrom the map object.Suggested changes to
DialogServiceclass: