-
-
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...'); | |
}); | |
} | |
} |
Excellent work. Thank you for creating such an elegant solution!
Thank you for nice elegant solution 👍
Cannot set property 'survey_move_dateModel' of undefined
html file
Pick Up Date:
<template [ngIf]="survey.survey_move_date!=''">
{{survey.survey_move_date | date:'dd MMM, y'}}
</template>
</p>
</div>
<div class="iconedit">
<a class="fancybox" (click)="openPickUpCalender()" *ngIf="!seedLoader">
<img src="../../../../images/edit-icn.png" >
</a>
</div>
</div>
open.component.ts
private stringToPickupObject(survey_move_date:string){
if(survey_move_date!='' && survey_move_date!=null){
let pkpDate:any =new Date(survey_move_date);
return {year:pkpDate.getFullYear() , month:pkpDate.getMonth()+1, day:pkpDate.getDate()};
}
}
openPickUpCalender() {
const NgbmodalRef: any = this.modalService.open(pickupcomponent, {size: 'sm'});
// this.survey.survey_move_date='2017-01-02'
NgbmodalRef.componentInstance.survey_move_dateModel = this.stringToPickupObject(this.survey.survey_move_date);
NgbmodalRef.componentInstance.survey_move_date = this.survey.survey_move_date;
NgbmodalRef.componentInstance.stateId = this.survey.survey_source_address.address_state_id;
NgbmodalRef.componentInstance.holidays = this.holidays;
NgbmodalRef.result.then((survey_move_date:any) => {
this.survey.survey_move_date = survey_move_date;
}, (reason:any) => {
// console.log('Date not selected')
})
}
pickup.component.ts
import {Component, Input} from '@angular/core';
import {
NgbModal, ModalDismissReasons, NgbActiveModal, NgbDatepickerConfig,
NgbDateStruct
} from '@ng-bootstrap/ng-bootstrap';
import {FormGroup, FormBuilder} from "@angular/forms";
@component({
selector: 'pickup-modal',
template: `
<button type="button" class="close" aria-label="Close" (click)="activeModal.close(null)">
×
Move Date
<div class="modal-body">
<ngb-datepicker #dp [(ngModel)]="survey_move_dateModel" name="move_date" #moveDate="ngbDatepicker"
(ngModelChange)="onPickupDateChange($event)"
(navigate)="date = $event.next" ></ngb-datepicker>
</div>
<div class="modal-footer">
<button class="btn-default" (click)="activeModal.close(survey_move_date)">Done</button>
`
})
export class PickUpModalComponent {
@Input()survey_move_dateModel: any;
@Input()survey_move_date: any;
@Input()stateId: number;
@Input()holidays: any[];
constructor(private pickupDateConfig: NgbDatepickerConfig, private fb: FormBuilder, private activeModal:NgbActiveModal) {
let currentDate: any = new Date();
let minDate: any = new Date();
let maxDate: any = new Date();
if (currentDate.getHours() > 12) {
minDate.setDate(currentDate.getDate() + 2);
maxDate.setDate(currentDate.getDate() + 92);
pickupDateConfig.minDate = {year: minDate.getFullYear(), month: minDate.getMonth() + 1, day: minDate.getDate()};
pickupDateConfig.maxDate = {year: maxDate.getFullYear(), month: maxDate.getMonth() + 1, day: maxDate.getDate()};
} else {
minDate.setDate(currentDate.getDate() + 1);
maxDate.setDate(currentDate.getDate() + 91);
pickupDateConfig.minDate = {year: minDate.getFullYear(), month: minDate.getMonth() + 1, day: minDate.getDate()};
pickupDateConfig.maxDate = {year: maxDate.getFullYear(), month: maxDate.getMonth() + 1, day: maxDate.getDate()};
}
// pickupDateConfig.outsideDays = 'hidden';
this.onPickupDateChange(this.survey_move_dateModel);
}
private onPickupDateChange(pickupDate: any) {
if (pickupDate != null && typeof pickupDate == 'object') {
this.survey_move_date = pickupDate.year + '-' + pickupDate.month + '-' + pickupDate.day;
}
}
isDisabled = (date: NgbDateStruct) =>
this.getDisabledDates(date.year, date.month, date.day);
private getDisabledDates(year: number, month: number, date: number) {
if (typeof this.holidays!= 'undefined' && this.stateId) {
return this.getHolidayDate(this.stateId, month, year).indexOf(date) > -1 ? true : false;
} else {
return false
}
}
private getHolidayDate(stateId: number, month: number, year: number) {
let stateHolidays: any[] = this.getOriginHolidays(stateId);
let holiDatesByMonth: any[] = [];
stateHolidays.forEach((function (holiDates: any) {
let d: any = new Date(holiDates.holiday_date);
if (month == d.getMonth() + 1 && year == d.getFullYear()) {
holiDatesByMonth.push(d.getDate());
}
}))
return holiDatesByMonth;
}
private getOriginHolidays(stateId: number) {
return this.holidays.filter(function (holiday: any) {
return holiday.state_id == stateId ? holiday : false;
})
}
}
can anyone help me in finding the error.
In Angular v4 template has been deprecated in favour of ng-template and completely removed in v5
@cnicho I'm getting the exact same problem as you. if any one has any luck please let us know
Change the code in your main app template to this:
<ng-template confirm>
<confirm-modal-component></confirm-modal-component>
</ng-template>
Then change the selector directive to this:
@Directive({
selector: "[confirm]"
})
That's working for me in Angular5
Example of how to use the directive, please.
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();
}`
Thank you very much.
I used it here