How to add custom buttons on Ng bootstrap datepicker popup?

Viewed 37

I went through NgBootstrap documentation but couldn't find anything helpful for my project that requires adding two extra buttons(reset and done) on the popup that appears on clicking the date picker popup. I'm able to override the CSS properties but when it comes to this it's not working. Please help!

This is my template:

<form class="row row-cols-sm-auto">
  <div class="col-5">
    <div class="dp-hidden position-absolute">
      <div class="input-group">
        <input
          name="datepicker"
          class="form-control"
          ngbDatepicker
          #datepicker="ngbDatepicker"
          [autoClose]="'outside'"
          (dateSelect)="onDateSelection($event)"
          [displayMonths]="2"
          [dayTemplate]="t"
          outsideDays="hidden"
          [startDate]="fromDate!"
          tabindex="-1"
        />

        <ng-template #t let-date let-focused="focused">
          <span
            class="custom-day"
            [class.focused]="focused"
            [class.range]="isRange(date)"
            [class.faded]="isHovered(date) || isInside(date)"
            (mouseenter)="hoveredDate = date"
            (mouseleave)="hoveredDate = null"
          >
            {{ date.day }}
          </span>
        </ng-template>
      </div>
    </div>
    <div class="input-group">
      <input
        #dpFromDate
        class="form-control"
        placeholder="yyyy-mm-dd"
        name="dpFromDate"
        [value]="formatter.format(fromDate)"
        (input)="fromDate = validateInput(fromDate, dpFromDate.value)"
      />
      <button
        class="btn btn-outline-secondary calendar"
        (click)="datepicker.toggle()"
        type="button"
      ></button>
    </div>
  </div>
  &nbsp;
  <div class="col-5">
    <div class="input-group">
      <input
        #dpToDate
        class="form-control"
        placeholder="yyyy-mm-dd"
        name="dpToDate"
        [value]="formatter.format(toDate)"
        (input)="toDate = validateInput(toDate, dpToDate.value)"
      />
      <button
        class="btn btn-outline-secondary calendar"
        (click)="datepicker.toggle()"
        type="button"
      ></button>
    </div>
  </div>
</form>

<p>From date model: {{ fromDate | json }}</p>
<p>To date model: {{ toDate | json }}</p>

Typescript:

import { Component, OnInit } from '@angular/core';
import {
  NgbDate,
  NgbCalendar,
  NgbDateParserFormatter,
} from '@ng-bootstrap/ng-bootstrap';

@Component({
  selector: 'app-datepicker-range-popup',
  templateUrl: './datepicker-range-popup.component.html',
  styleUrls: ['./datepicker-range-popup.component.scss'],
})
export class DatepickerRangePopupComponent {
  hoveredDate: NgbDate | null = null;

  fromDate: NgbDate | null;
  toDate: NgbDate | null;

  constructor(
    private calendar: NgbCalendar,
    public formatter: NgbDateParserFormatter
  ) {
    this.fromDate = calendar.getToday();
    this.toDate = calendar.getNext(calendar.getToday(), 'd', 5);
  }

  onDateSelection(date: NgbDate) {
    if (!this.fromDate && !this.toDate) {
      this.fromDate = date;
    } else if (
      this.fromDate &&
      !this.toDate &&
      date &&
      date.after(this.fromDate)
    ) {
      this.toDate = date;
    } else {
      this.toDate = null;
      this.fromDate = date;
    }
  }
  isHovered(date: NgbDate) {
    return (
      this.fromDate &&
      !this.toDate &&
      this.hoveredDate &&
      date.after(this.fromDate) &&
      date.before(this.hoveredDate)
    );
  }

  isInside(date: NgbDate) {
    return this.toDate && date.after(this.fromDate) && date.before(this.toDate);
  }

  isRange(date: NgbDate) {
    return (
      date.equals(this.fromDate) ||
      (this.toDate && date.equals(this.toDate)) ||
      this.isInside(date) ||
      this.isHovered(date)
    );
  }

  validateInput(currentValue: NgbDate | null, input: string): NgbDate | null {
    const parsed = this.formatter.parse(input);
    return parsed && this.calendar.isValid(NgbDate.from(parsed))
      ? NgbDate.from(parsed)
      : currentValue;
  }
}
1 Answers

you can achieve it using footer template documentation link

Here is sample template app.component.html

<form class="row row-cols-sm-auto">
  <div class="col-12">
    <div class="input-group">
      <input
        class="form-control"
        placeholder="yyyy-mm-dd"
        [autoClose]="'outside'"
        name="dp"
        [(ngModel)]="model"
        ngbDatepicker
        [footerTemplate]="footerTemplate"
        #d="ngbDatepicker"
      />

      <button
        class="btn btn-outline-secondary calendar"
        (click)="d.toggle()"
        type="button"
      ></button>
    </div>
  </div>
</form>

<ng-template #footerTemplate>
  <hr class="my-0" />
  <button
    class="btn btn-secondary btn-sm m-2 float-start"
    (click)="model = null;"
  >
    Reset
  </button>
  <button class="btn btn-primary btn-sm m-2 float-end" (click)="d.close()">
    Done
  </button>
</ng-template>

and file app.component.ts

import {Component} from '@angular/core';
import {NgbCalendar, NgbDateStruct} from '@ng-bootstrap/ng-bootstrap';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  model: NgbDateStruct;

  constructor( ) {}
}

Note: the clear selection feature is still open bug on ng-bootstrap github.

Related