How to pick date range in angular material 5.0.0 with datepicker?

Viewed 58211

I am using the latest Angular Material 5.0.0-rc0 in my Angular 5 app. I am trying to select a range of dates with the datepicker provided with Angular material, but I couldn't find any documentation regarding that.

All I could work with it is to select a startDate or set the minDate and maxDate. Here's the HTML code for that

<mat-form-field>
  <input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="picker" placeholder="Choose a date">
  <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
  <mat-datepicker #picker startView="year" [startAt]="startDate"></mat-datepicker>
</mat-form-field>

and TS code

import {Component} from '@angular/core';

@Component({
  selector: 'datepicker-start-view-example',
  templateUrl: 'datepicker-start-view-example.html',
  styleUrls: ['datepicker-start-view-example.css'],
})
export class DatepickerStartViewExample {
  minDate = new Date(2000, 0, 1);
  maxDate = new Date(2020, 0, 1);
}

This code helps me select one date in the range of those min and max dates but doesn't allow to select a range of them. Though using a third-party library may solve this problem, but its gonna have a different UI, which is going to be different from my current app's UI. Please help me solve this issue.

10 Answers

Recommend to check out Saturn Material range Datepicker. Have a look also at their demo page. It is a full Material UX with built-in support for your existing Material theme.

You can install it with npm install saturn-datepicker. See their Github page for full integration instructions.

Markup looks like this:

  <mat-form-field>
    <input matInput
        placeholder="Choose a date"
        [satDatepicker]="picker"
        [value]="date">
    <sat-datepicker #picker [rangeMode]="true"></sat-datepicker>
    <sat-datepicker-toggle matSuffix [for]="picker"></sat-datepicker-toggle>
  </mat-form-field>

And here is what it ends up looking like on the page:

Screenshot of the Material date range picker

There's currently no possible way to select a range of dates, although there's an issue requesting so.

In the mean time you could just have two date pickers. One for the start date and one for the end date. Something like this:

<mat-form-field> //start date datepicker
  <input matInput [min]="minDate" [max]="maxDate" [formControl]="startDate" [matDatepicker]="picker" placeholder="Choose a start date">
  <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
  <mat-datepicker #picker startView="year" [startAt]="minDate"></mat-datepicker>
</mat-form-field>

<mat-form-field> //end date datepicker
  <input matInput [min]="startDate" [max]="maxDate" [formControl]="endDate" [matDatepicker]="picker" placeholder="Choose a date">
  <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
  <mat-datepicker #picker startView="year" [startAt]="startDate"></mat-datepicker>
</mat-form-field>

You could then have some where if they change the endDate to something before the startDate it shows them an error or resets the startDate

It is even not possible in Angular Material 7.0.0. There are different plugins to do so. I came up with ngx-aaa-datepicker. It worked for me.

Preview

You can check this out in NPM

It is totally possible to do it but not directly with mat-datepicker component. Actually, mat-datepicker uses mat-menu component to display a mat-calendar component.

To perform what you ask, you could use something like below.

In your component.html file

<mat-menu #startDateRangeMenu="matMenu">
  <mat-calendar 
      #calendar 
      (click)="$event.stopPropagation()"
      (selectedChange)="toggleStartDateDate($event, calendar)"
      [dateClass]="isStartDateDateSelected()">
  </mat-calendar>
</mat-menu>

<button mat-icon-button [matMenuTriggerFor]="startDateRangeMenu">
   {{'startDate' | translate}}
</button>

In your .ts file

isStartDateSelected() {
    return (date: Date): MatCalendarCellCssClasses => {
      const dateMoment = moment(date);
      let startDate;
      let endDate;
      if (this.startDateAfter) {
        startDate = moment(this.startDateAfter);
      }
      if (this.startDateBefore) {
        endDate = moment(this.startDateBefore);
      }

      if (startDate && endDate && dateMoment.isBetween(startDate, endDate, 'days', '[]')) {
        return 'date-selected';
      } else if (startDate && dateMoment.isSame(startDate)) {
        return 'date-selected';
      } else if (moment().startOf('day').isSame(moment(date).startOf('day'))) {
        return 'today';
      }
      return;
    };
  }

 toggleStartDateDate(date: any, calendar: any) {
    const dateString = moment(date).format('YYYY-MM-DD');
    if (this.selectStartDateType === 'startDateAfter') {
      this.startDateAfter = dateString;
      this.startDateBefore = dateString;
      this.selectStartDateType = 'startDateBefore';
    } else {
      if (moment(date).isBefore(moment(this.startDateAfter))) {
        this.startDateAfter = dateString;
        this.startDateBefore = dateString;
        this.selectStartDateType = 'startDateBefore';
      } else {
        this.startDateBefore = dateString;
        this.selectStartDateType = 'startDateAfter';
      }
    }
    calendar.updateTodaysDate();
  }

In your .css file

::ng-deep .date-selected .mat-calendar-body-cell-content {
  background-color: #17a2b8;
  color: white;
}

::ng-deep .date-selected.mat-calendar-body-cell:hover .mat-calendar-body-cell-content {
  background-color: #17a2b8 !important;
}

May be this can help you:

<mat-form-field>
    <mat-label>Custom date</mat-label>
    <input matInput type="datetime-local">
</mat-form-field> 

Yes, it is possible by using mat-calendar. You need to use DateRange and pass it to the selected attribute of mat-calendar. After doing this you need to handle some cases on selectedChange method of mat-calendar. Working example is given below:

In html file

 <mat-calendar [selected]="selectedDate"
                          (selectedChange)="onSlotChange($event)">
 </mat-calendar>

In ts file declare and add following method

selectedDate: DateRange<Date>;
      
     onSlotChange(changedDate: Date) {
        if (!this.selectedDate) {
            this.selectedDate = new DateRange(changedDate, changedDate);
        } else if (this.selectedDate.start > changedDate && !this.selectedDate.end) {
            this.selectedDate = new DateRange(changedDate, this.selectedDate.start);
        } else if (this.selectedDate.start > changedDate && this.selectedDate.end) {
            this.selectedDate = new DateRange(changedDate, this.selectedDate.end);
        } else {
            this.selectedDate = new DateRange(this.selectedDate.start, changedDate);
        }
    }

This will do your work. All the best!

Related