I am working with Angular 5.2 and ngbootstrap 1.1. The library has a couple of different datepicker options one that serves as a component and another that serves as a directive.
I am working with the directive. Below is a link to an example of how to setup ngbootstrap's directive with a template.
directive example
When two dates are selected the popover adds one date inside of the input.
In my initial approach I attempted to set
[(ngModel)]=model
and
this.model = `${this.fromDate} / ${this.toDate}`;
However, I realized that after setting model. The date was checked inside of another component provided by ngbootstrap's datepicker module.
private _fromDateStruct(date: NgbDateStruct): NgbDate {
const ngbDate = date ? new NgbDate(date.year, date.month, date.day) : null;
return this._calendar.isValid(ngbDate) ? ngbDate : null;
}
link to component code
Also the private method above makes it impossible to extend the class and override the check
class DateInputDirective extends NgbInputDatepicker
I dug a little deeper and hoped that I could change a service. NgbDateAdapter service returns the object or null if it is not accurate. My string date range could not get past this.
NgBootstrap API
fromModel(date: NgbDateStruct): NgbDateStruct {
return (date && date.year && date.month && date.day) ? {year: date.year, month: date.month, day: date.day} : null;
}
Also, I took advantage of angular's useClass and slipped a string conditional.
{
provide: NgbDateAdapter,
useClass: NgbStringAdapter
},
However, the NgbDatePickerInput class overrode the behavior before this class finalized.
Not sure if I am missing something very simple Or if it is a matter of using extends or useClass a few more times. But I am definitely looking for some insight on this. The stackblitz link is not my actual code but is a good representation of what I started with. Feel free to reach out if something was not written clear. Thanks!
Using ElementRef
and ViewChild
to set the input value would work.
I mean not using ViewChild
with the Directive/Component( NgbInputDatepicker
), because all accessor to the element from the directive are private and I dont see how to change the input from there unless in _writeModelValue
method (line 363) , writeValue
call it, but it only update one date.
So the steps are:
ElementRef
and ViewChild
to having the input element,Renderer2
for setting its value (like the directive do)NgbDateParserFormatter
to format the model date onDateSelection
This is the main code, I'll add some comments:
HTML:
<input
#myRangeInput
class="form-control"
placeholder="mm/dd/yyyy"
name="dp"
[(ngModel)]="model"
ngbDatepicker
[dayTemplate]="t"
[autoClose]="false"
[displayMonths]="2"
[maxDate]="maxDate"
[minDate]="minDate"
>
<ng-template #t let-date="date" let-focused="focused">
<span class="custom-day"
[class.range]="isFrom(date) || isTo(date) || isInside(date) || isHovered(date)"
[class.faded]="isHovered(date) || isInside(date)"
(click)="onDateSelection(date)"
(mouseenter)="hoveredDate = date"
(mouseleave)="hoveredDate = null"
>
{{ date.day }}
</span>
</ng-template>
TYPESCRIPT:
import {Component, ViewChild, OnInit, ElementRef, Renderer2} from '@angular/core';
import {
NgbDatepicker,
NgbInputDatepicker,
NgbDateStruct,
NgbCalendar,
NgbDateAdapter,
NgbDateParserFormatter} from '@ng-bootstrap/ng-bootstrap';
...
export class NgbdDatepickerPopup implements OnInit{
...
@ViewChild('myRangeInput') myRangeInput: ElementRef;
constructor(element: ElementRef, private renderer: Renderer2, private _parserFormatter: NgbDateParserFormatter) { }
onDateSelection(date: NgbDateStruct) {
let parsed = ''; // initializing with empty string
if (!this.fromDate && !this.toDate) {
this.fromDate = date;
} else if (this.fromDate && !this.toDate && after(date, this.fromDate)) {
this.toDate = date;
this.input.close();
} else {
this.toDate = null;
this.fromDate = date;
}
if(this.fromDate) {
// if fromDate is set: add the first date
parsed += this._parserFormatter.format(this.fromDate);
}
if(this.toDate) {
// if toDate is set: add the second date with separator
parsed += ' - ' + this._parserFormatter.format(this.toDate);
}
// here we update the input value with the new parsed value
this.renderer.setProperty(this.myRangeInput.nativeElement, 'value', parsed);
}
And this is the working demo: https://stackblitz.com/edit/angular-skbpc8-maun8a
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