-
-
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...'); | |
}); | |
} | |
} |
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();
}`
Hello, this is a great solution but it doesn't seem to work for templates containing relative links/anchors. Having the component that registers the templates defined in the application root makes href act relative to root. The same goes for angular router in the component itself. Do you know of any way that would make relative links work?
In typical fashion, I found the solution right after posting.
I tried many ways including using absolute urls, but used the window href instead. This caused an unwanted page refresh.
One very simple way to do it that took me way too long to find is:
<a [routerLink]="window.location.pathname + '/' + relative_link">name</a>
@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. :-)