-
-
Save jnizet/15c7a0ab4188c9ce6c79ca9840c71c4e to your computer and use it in GitHub Desktop.
import { Component, Injectable, Directive, TemplateRef } from '@angular/core'; | |
import { NgbModal, NgbModalRef, NgbModalOptions } from '@ng-bootstrap/ng-bootstrap'; | |
/** | |
* Options passed when opening a confirmation modal | |
*/ | |
interface ConfirmOptions { | |
/** | |
* The title of the confirmation modal | |
*/ | |
title: string, | |
/** | |
* The message in the confirmation modal | |
*/ | |
message: string | |
} | |
/** | |
* An internal service allowing to access, from the confirm modal component, the options and the modal reference. | |
* It also allows registering the TemplateRef containing the confirm modal component. | |
* | |
* It must be declared in the providers of the NgModule, but is not supposed to be used in application code | |
*/ | |
@Injectable() | |
export class ConfirmState { | |
/** | |
* The last options passed ConfirmService.confirm() | |
*/ | |
options: ConfirmOptions; | |
/** | |
* The last opened confirmation modal | |
*/ | |
modal: NgbModalRef; | |
/** | |
* The template containing the confirmation modal component | |
*/ | |
template: TemplateRef<any>; | |
} | |
/** | |
* A confirmation service, allowing to open a confirmation modal from anywhere and get back a promise. | |
*/ | |
@Injectable() | |
export class ConfirmService { | |
constructor(private modalService: NgbModal, private state: ConfirmState) {} | |
/** | |
* Opens a confirmation modal | |
* @param options the options for the modal (title and message) | |
* @returns {Promise<any>} a promise that is fulfilled when the user chooses to confirm, and rejected when | |
* the user chooses not to confirm, or closes the modal | |
*/ | |
confirm(options: ConfirmOptions): Promise<any> { | |
this.state.options = options; | |
this.state.modal = this.modalService.open(this.state.template); | |
return this.state.modal.result; | |
} | |
} | |
/** | |
* The component displayed in the confirmation modal opened by the ConfirmService. | |
*/ | |
@Component({ | |
selector: 'confirm-modal-component', | |
template: `<div class="modal-header"> | |
<button type="button" class="close" aria-label="Close" (click)="no()"> | |
<span aria-hidden="true">×</span> | |
</button> | |
<h4 class="modal-title">{{ options.title}}</h4> | |
</div> | |
<div class="modal-body"> | |
<p>{{ options.message }}</p> | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-danger" (click)="yes()">Yes</button> | |
<button type="button" class="btn btn-secondary" (click)="no()">No</button> | |
</div>` | |
}) | |
export class ConfirmModalComponent { | |
options: ConfirmOptions; | |
constructor(private state: ConfirmState) { | |
this.options = state.options; | |
} | |
yes() { | |
this.state.modal.close('confirmed'); | |
} | |
no() { | |
this.state.modal.dismiss('not confirmed'); | |
} | |
} | |
/** | |
* Directive allowing to get a reference to the template containing the confirmation modal component, | |
* and to store it into the internal confirm state service. Somewhere in the view, there must be | |
* | |
* ``` | |
* <template confirm> | |
* <confirm-modal-component></confirm-modal-component> | |
* </template> | |
* ``` | |
* | |
* in order to register the confirm template to the internal confirm state | |
*/ | |
@Directive({ | |
selector: "template[confirm]" | |
}) | |
export class ConfirmTemplateDirective { | |
constructor(confirmTemplate: TemplateRef<any>, state: ConfirmState) { | |
state.template = confirmTemplate; | |
} | |
} | |
@Component({ | |
selector: 'some-applicative-component', | |
templateUrl: './some-applicative-component.html' | |
}) | |
export class SomeApplicativeComponent { | |
constructor(private confirmService: ConfirmService) {} | |
deleteFoo() { | |
this.confirmService.confirm({ title:'Confirm deletion', message: 'Do you really want to delete this foo?' }).then( | |
() => { | |
console.log('deleting...'); | |
}, | |
() => { | |
console.log('not deleting...'); | |
}); | |
} | |
} |
Very thank you!
Hi there,
thanks a lot buddy to sharing :)
Based on your example I've made a simple module
to use with ie CanDeactivate
https://gist.github.com/whisher/c5726e30ea40a4d5caf8b77ab8b0d48a
Here is a working sample on Angular 7 for anyone having issues. The directive needs to be included in the declarations.
https://stackblitz.com/edit/angular-muowrf
@jnizet Thank you for the awesome sample!
@tshannon Thanks for the nudge in the right direction for Angular 5.
I used the code from the gist and moved it into a StackBlitz project. I split the separate items into their own files, optimized the references and dependencies, and added a few more options to test the pattern. I then wrapped the confirmation dialog code into a separate NgModule for optimal reuse. It should be drop-in ready for use in another project.
Please note that I used the package versions I did because that's what was pre-existing in my project. Your mileage may vary. :-)
@pflugs30 you shouldn't use this code. It was a workaround for the lack of component support in ngb modals a long time ago.
Here's a better solution: https://github.com/Ninja-Squad/globe42/blob/master/frontend/src/app/confirm.service.ts, https://github.com/Ninja-Squad/globe42/tree/master/frontend/src/app/confirm-modal-content.
@jnizet Thanks for the update. I will take a look at that other link you posted. Much appreciated!
Edit: I am aware of the limitations of the NgbModals. In my current project, I need to use Angular 5 and NgbModals v1.1.2 (as I showed in my StackBlitz). Until I have the time to update my app to the latest packages, I figured your excellent example would be most helpful. :-)
hello,
i used this inside form valueschanges but i had ExpressionChangedAfterItHasBeenCheckedError exception due to Expression has changed after it was checked. Previous value: 'ng-untouched: true'. Current value: 'ng-untouched: false'. Do you have any idea please?
@omarouen You should ask a question, with a complete minimal example reproducing the issue, on StackOverflow.
@omarouen - try to add a change detection strategy to your app as follows.
`import { ChangeDetectorRef, AfterContentChecked} from '@angular/core';
constructor(
private cdref: ChangeDetectorRef) { }
ngAfterContentChecked() {
this.cdref.detectChanges();
}`
Example of how to use the directive, please.