Multiple Date Select in Material datePicker (Angular)

15,446

Solution 1

You need work directly with the mat-calendar, you can enclosed in a mat menu and into a div to avoid "closed", see

<button mat-icon-button [matMenuTriggerFor]="appMenu">
  <mat-icon>calendar_today</mat-icon>
</button>
<mat-menu #appMenu="matMenu">
    <div (click)="$event.stopPropagation()">
        <mat-calendar #calendar 
           (selectedChange)="select($event,calendar)" 
            [dateClass]="isSelected">
        </mat-calendar>
    </div>
</mat-menu>

I choose store the values of the dates in a string in the way yyyy-MM-dd (*), so

Imports:

import { Component,ViewEncapsulation} from "@angular/core";

TS Code:

daysSelected: any[] = [];
event: any;

isSelected = (event: any) => {
  const date =
    event.getFullYear() +
    "-" +
    ("00" + (event.getMonth() + 1)).slice(-2) +
    "-" +
    ("00" + event.getDate()).slice(-2);
  return this.daysSelected.find(x => x == date) ? "selected" : null;
};

select(event: any, calendar: any) {
  const date =
    event.getFullYear() +
    "-" +
    ("00" + (event.getMonth() + 1)).slice(-2) +
    "-" +
    ("00" + event.getDate()).slice(-2);
  const index = this.daysSelected.findIndex(x => x == date);
  if (index < 0) this.daysSelected.push(date);
  else this.daysSelected.splice(index, 1);

  calendar.updateTodaysDate();
}

Finally the .css is simple:

.mat-calendar-body-cell.selected
{
  background-color:red!important;
  border-radius: 50%
}
.drop-calendar
{
  width:30rem
}

NOTE: Dont forget to set encapsulation to none in your component:

encapsulation:ViewEncapsulation.None

Update Why use ViewEncapsulation.None and other aproach use in styles.css

The problem is how put color to the date selected. When we use in mat-calendar [dateclass], we create a function that received as parameter the date (of each day of the month) and return a string with the name of the class you want. In the code, if the day is in the array selected, the class is 'selected'.

But this don't take account if we don't use ViewEncapsulation.None or we put in the styles.css (or styles.scss) (**). Yes, it's necesary that this style was defined in a "global" style. (remember that ViewEncapsulation.None make that the styles defined in the component becomes "global"

NOTE: If you "play" in stackblitz with ViewEncapsulation.None remember that you need refresh the stackblitz because the styles remain saved.

(**) remember in angular.json include in "styles"

"styles": [
 "src/styles.scss"
],

You can see in stackblitz

(*) you can choose, e.g. store the getTime() of the date selected. The idea is that you need find it in the array "daysSelected", else, if you use directy an object Date, you need compare year, month and day from date to the elements of the array. This give a poor perfomance. Think that the function "isSelected" is called how many times as days has a month, each time a click is done

Solution 2

One more way (kinda hack): StackBlitz

Just temporary rewriting close method to empty function, and return it back after change. Also calling weekdays rerendering function. Not safe and ideal solution, but works.

Might be useful for somebody.

UPD: Or, you can use ngx-multiple-dates package. There are some examples of it.

Share:
15,446
Vinoth A
Author by

Vinoth A

Updated on July 02, 2022

Comments

  • Vinoth A
    Vinoth A almost 2 years

    I have a requirement that a user can select multiple dates in a date picker. How can I implement multiple date select functionality in an Angular Material date picker?

    date picker

    I tried this through dateClass. But, after every date selection the date picker will be closed.

    Here's what I tried

    HTML code:

    <input matInput [matDatepicker]="picker" placeholder="Choose a date">
    <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
    <mat-datepicker [dateClass]="dateClass" #picker></mat-datepicker>
    

    Typescript code:

    dateClass = (d: Date) => {
        const date = d.getDate();
    
        // Highlight the 1st and 20th day of each month.
        return (date === 1 || date === 5 || date === 14 || date === 19 || date === 21 ) ? 'example-custom-date-class' : undefined;
    }
    
  • Prashant Pimpale
    Prashant Pimpale over 4 years
    you just nailed it! Good one:)
  • Eliseo
    Eliseo over 4 years
    @PrashantPimpale, thank for the correct, I just removed unnecesary imports and variables
  • Vinoth A
    Vinoth A over 4 years
    thanks @Eliseo. Your solution was so helpful for me and solved my issue.
  • Shardul
    Shardul about 4 years
    After using encapsulation:ViewEncapsulation.None all my styles are getting affected. Is there any simple solution to it? If I use ::ng-deep the above code working without using encapsulation:ViewEncapsulation.None but another catch is ::ng-deep is depricated. :(
  • Arthez
    Arthez about 4 years
    @Eliseo pelase explain why adding encapsulation:ViewEncapsulation.None is important? I don't see any reason you have to add this in order to make it work. I removed it from your example code and nothing has changed.
  • Eliseo
    Eliseo about 4 years
    @Arthez, I try to explain in the updated of the answer. Basically you need put the class "global". You can also simple change the styles.css (or the styles.scss) and forget the ViewEncapsulation.None
  • Ruslan Lekhman
    Ruslan Lekhman about 3 years