Add date range to NgbInputDatepicker input field

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


The above answer is correct and I would had made this a comment had I the required reputation but since I cant, here is a snippet of css you can add to styles.css to complete the functionality.

.ngb-dp-day.disabled[_ngcontent-c21], .ngb-dp-day.hidden[_ngcontent-c21] {
  cursor: default;
  color: #d0d0d0;
  pointer-events: none;
  -webkit-user-select: none; /* Chrome all / Safari all */
  -moz-user-select: none;   /* Firefox all */
  -ms-user-select: none;  /* IE 10+ */
   user-select: none;  /* Likely future */
}

This will make the disabled by min/maxDate or by markDisabled dates to look faded and unselectable.