The Date object in JavaScript does not have the methods isBefore or isSame. These are methods of the momentjs library, specifically of the object type moment.
If you want to achieve something similar, you could do:
if (moment(date).isBefore(moment()))
return 'disabled-dates'
return this.dates.find((x) => moment(date).isSame(x,'day'))? 'selected' :
'not-selected';
(It's not necessary to filter, simply find the first date that fulfills the condition)
However, it's important to note that using momentjs may not be necessary.
When dealing with dates, one should take into consideration factors such as UTC, format, etc.
Furthermore, the "dateClass" function (your function isSelect) is executed for each day displayed on the calendar, so it should be optimized for performance. It might be helpful to use console.log(date) to see the value.
console.log(date) //'Fri Jan 20 2023 00:00:00 GMT+0100'
This means our dates are at 00:00:00.
Let's imagine your service returns an array of strings:
['2023-01-05','2023-01-12','2023-01-26']
We are working with two variables:
dates:number[]
today:number
Yes, numbers. We are going to convert the array of strings into arrays of numbers like this:
ngOnInit() {
this.dataService.getDates().subscribe((res: any[]) => {
const now = new Date();
now.setHours(0, 0, 0);
this.today = now.getTime();
this.dates = res.map((x) => {
const date = new Date(x);
date.setHours(0, 0, 0);
return date.getTime();
});
});
}
Now our function looks like this:
dateClass: MatCalendarCellClassFunction<Date> = (date: Date, view) => {
const time = date.getTime();
if (time < this.today) return 'disabled-dates';
return this.dates.find((x: number) => x == time)
? 'selected'
: 'not-selected';
};
For more details, check out the StackBlitz. If you don't use encapsulation: ViewEncapsulation.None, make sure to declare the .css in styles.scss.
Update: Using an API that returns selected days in a month
If you are using an API that only retrieves dates selected for one month at a time, you cannot use dateClass. In this case, you need to handle it differently using a "JavaScript way."
First, define a panelClass for your matInput datePicker and add the "opened" event:
<mat-form-field class="example-full-width" appearance="fill">
<mat-label>Choose a date</mat-label>
<input matInput [matDatepicker]="picker2">
<mat-hint>MM/DD/YYYY</mat-hint>
<mat-datepicker-toggle matIconSuffix [for]="picker2"></mat-datepicker-toggle>
<mat-datepicker [dateClass]="dateClassAsync" #picker2 panelClass="picker2" (opened)="open(picker2.startAt)"></mat-datepicker>
</mat-form-field>
The dateClassAsync is used to capture the new month:
dateClassAsync: MatCalendarCellClassFunction<Date> = (date: Date, view) => {
if (date.getDate() == 1) {
setTimeout(()=>{
this.showMonth(date);
})
}
return '';
};
Then, implement the open function:
open(date: any) {
date = date || new Date();
setTimeout(()=>{
this.showMonth(date);
})
}
Finally, create the showMonth function to manipulate the calendar display based on the retrieved data:
showMonth(date: any) {
this.dataService
.getDateMonth(date.getMonth() + 1)
.subscribe((dates: string[]) => {
const cal = document.getElementsByClassName('picker2')[0];
const days = cal.getElementsByClassName('mat-calendar-body-cell');
const yearmonth =
date.getFullYear() +
'-' +
('00' + (date.getMonth() + 1)).slice(-2) +
'-';
const today = new Date();
const todayTxt =
today.getFullYear() +
'-' +
('00' + (today.getMonth() + 1)).slice(-2) +
'-' +
('00' + today.getDate()).slice(-2);
for (var i = 0; i < days.length; i++) {
const dateTxt =
yearmonth + ('00' + i).slice(-2);
console.log(dateTxt)
if (dateTxt < todayTxt) days[i].classList.add('disabled-dates');
else
days[i].classList.add(
dates.find((x: string) => x == dateTxt)
? 'selected'
: 'not-selected'
);
}
});
}