Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add date range to NgbInputDatepicker input field

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!

like image 398
Winnemucca Avatar asked Jun 14 '18 05:06

Winnemucca


1 Answers

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:

  • by using ElementRef and ViewChild to having the input element,
  • and Renderer2 for setting its value (like the directive do)
  • and using NgbDateParserFormatter to format the model date
  • You just need to do this when date is selected, so in the method 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

like image 60
Fetrarij Avatar answered Sep 24 '22 12:09

Fetrarij