Skip to content

Instantly share code, notes, and snippets.

@bartwttewaall
Last active December 17, 2019 12:56
Show Gist options
  • Save bartwttewaall/8b430a582709cac46d9455c49706c16f to your computer and use it in GitHub Desktop.
Save bartwttewaall/8b430a582709cac46d9455c49706c16f to your computer and use it in GitHub Desktop.
Angular Material mat-datepicker custom date validation and filter rules with extensible settings
const daysOffset = 1; // next day delivery
const deadlineTime = 17 * 3600; // 17:00 in seconds
const unavailableDays = [0, 1]; // sunday = 0, monday = 1
const unavailableDates = [
/* 2019 */
{ dateString: '2019-01-01', name: 'Nieuwjaarsdag' },
{ dateString: '2019-04-01', name: '1e paasdag' },
{ dateString: '2019-04-22', name: '2e paasdag' },
{ dateString: '2019-04-27', name: 'Koningsdag' },
{ dateString: '2019-05-30', name: 'Hemelvaartsdag' },
{ dateString: '2019-06-09', name: '1e pinksterdag' },
{ dateString: '2019-06-10', name: '2e pinksterdag' },
{ dateString: '2019-12-25', name: '1e kerstdag' },
{ dateString: '2019-12-26', name: '2e kerstdag' },
/* 2020 */
{ dateString: '2020-01-01', name: 'Nieuwjaarsdag' },
{ dateString: '2020-04-10', name: 'Goede vrijdag' },
{ dateString: '2020-04-12', name: '1e paasdag' },
{ dateString: '2020-04-13', name: '2e paasdag' },
{ dateString: '2020-04-27', name: 'Koningsdag' },
{ dateString: '2020-05-05', name: 'Bevrijdingsdag' },
{ dateString: '2020-05-21', name: 'Hemelvaartsdag' },
{ dateString: '2020-05-31', name: '1e pinksterdag' },
{ dateString: '2020-06-01', name: '2e pinksterdag' },
{ dateString: '2020-12-25', name: '1e kerstdag' },
{ dateString: '2020-12-26', name: '2e kerstdag' }
];
export function getDateOffset(): number {
const beforeDeadline = getCurrentTimeInSeconds() < deadlineTime;
return daysOffset + (beforeDeadline ? 0 : 1);
}
export function getValidNextDate() {
return dateAddDays(new Date(), getDateOffset());
}
export function getFirstAvailableDate(startDate: Date): Date {
let date = new Date(startDate);
let isValidDate = validateShippingDate(date);
let numTries = 0;
while (!isValidDate && ++numTries < 365) {
// try the next day
date = dateAddDays(date, 1);
isValidDate = validateShippingDate(date);
}
return date;
}
export const validateShippingDate = (date: Date) => {
if (unavailableDays.includes(date.getDay())) return false;
const dateString = getDateString(date);
return (
unavailableDates.filter(entry => {
return entry.dateString === dateString;
}).length === 0
);
}
// ---- utils ----
export function dateAddDays(date: Date, offset: number = 1): Date {
const clone = new Date(date);
clone.setDate(clone.getDate() + offset);
return clone;
}
export function getCurrentTimeInSeconds(): number {
const now = new Date();
return now.getHours() * 3600 + now.getMinutes() * 60 + now.getSeconds();
}
// DO NOT use date.toISOString().slice(0, 10). It doesn't keep the timezone in mind
export function getDateString(date: Date) {
return [date.getFullYear(), date.getMonth() + 1, date.getDate()]
.map(entry => (entry.toString().length === 1 ? '0' + entry : entry))
.join('-');
}
<mat-card class="shipping-card">
<mat-card-header>
<mat-card-title>Shipping</mat-card-title>
</mat-card-header>
<mat-card-content>
<form class="form-vertical" [formGroup]="form">
<p>When you order before 17:00 hours we'll be able ship this very evening and you can expect a next day delivery.</p>
<div class="form-group">
<label>Bezorgdag</label>
<mat-form-field appearance="outline">
<input
matInput
formControlName="shippingDate"
[matDatepickerFilter]="shippingDateFilter"
[matDatepicker]="picker"
[min]="shippingDateMin"
[max]="shippingDateMax"
(inputChange)="onShippingDateChanged($event)"
(dateChange)="onShippingDateChanged($event)"
/>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
</div>
</form>
</mat-card-content>
</mat-card>
import { Component, OnInit, Input } from '@angular/core';
import { MatDatepickerInputEvent } from '@angular/material';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { validateShippingDate, getFirstAvailableDate, getValidNextDate, dateAddDays } from './shipping-date.util';
@Component({
selector: 'page-shipping',
templateUrl: './shipping.component.html',
styleUrls: ['./shipping.component.scss']
})
export class PageShippingComponent implements OnInit {
@Input()
selectedDate: Date;
shippingDateMin: Date;
shippingDateMax: Date;
shippingDateFilter = validateShippingDate;
form: FormGroup = this.fb.group({
shippingDate: new FormControl('', Validators.required)
});
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.shippingDateMin = getFirstAvailableDate(getValidNextDate());
this.shippingDateMax = dateAddDays(this.shippingDateMin, 180);
const useSelectedDate = this.selectedDate && this.selectedDate > this.shippingDateMin;
const shippingDate = useSelectedDate ? getFirstAvailableDate(this.selectedDate) : this.shippingDateMin;
this.form.get('shippingDate').setValue(shippingDate);
}
onShippingDateChanged(evt: MatDatepickerInputEvent<Date>) {
console.log(evt.value);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment