All tested using Angular 14 and Material 14
This code is unique because it allows for MatDatePicker formats to be changed on an individual use basis, requires no external libraries (by using the built-in Angular DatePipe
), and defaults to MAT_NATIVE_DATE_FORMATS
for all other dates shown in the date picker (besides the one visible in the input, which is what we are customizing). To see those defaults specified by MAT_NATIVE_DATE_FORMATS
, check out its definition on Github.
- Create a custom
DateAdapter
which extends NativeDateAdapter
and overrides the parent format()
function, but falls back on the parent via super.format()
if no custom format string is provided.
import { DatePipe } from '@angular/common';
import { Injectable } from '@angular/core';
import { MAT_NATIVE_DATE_FORMATS, MatDateFormats, NativeDateAdapter } from '@angular/material/core';
@Injectable()
export class DatePipeDateAdapter extends NativeDateAdapter {
override format(date: Date, displayFormat: Object): string {
// Use DatePipe to format date however you specify
if (typeof displayFormat === 'string') {
return new DatePipe('en-US').transform(date, displayFormat) as string;
}
// Default to parent class format() if no custom format string is given
return super.format(date, displayFormat);
}
// This function creates a custom `MatDateFormats` object that
// defaults all values to `MAT_NATIVE_DATE_FORMATS` except for
// the `display.dateInput` property, which gets set by the user
// via this `displayFormat` parameter. This parameter ultimately
// gets passed to the Angular `DatePipe` in the `format()`
// function above, so it can be any format value that `DatePipe`
// accepts:
// https://angular.io/api/common/DatePipe#usage-notes
static createCustomMatDateFormats(displayFormat: string): MatDateFormats {
const customDateInputFormats: MatDateFormats = {
...MAT_NATIVE_DATE_FORMATS,
display: {
...MAT_NATIVE_DATE_FORMATS.display,
dateInput: displayFormat,
}
}
return customDateInputFormats;
}
}
- Provide our custom
DateAdapter
and MAT_DATE_FORMATS
. By providing these in the component, only MatDatePickers used inside this component will be affected. If you want all MatDatePickers to be affected, provide these inside AppModule
.
@Component({
...,
providers: [
{ provide: DateAdapter, useClass: DatePipeDateAdapter },
{
provide: MAT_DATE_FORMATS,
// Pass any format string you would pass to DatePipe
useValue: DatePipeDateAdapter.createCustomMatDateFormats('EEE, MMM dd, yyyy'),
},
],
})
In my particular use-case, I have the <input>
set to readonly, so the user is forced to change the date via the date picker by clicking on its surrounding <mat-form-field>
or its connected <mat-datepicker-toggle>
.
<mat-form-field (click)="crewDate.open()">
<input type="text" placeholder="Date of Crews"
readonly
matInput
[formControl]="this.crewDateControl"
[matDatepicker]="crewDate"
>
<mat-datepicker-toggle matSuffix [for]="crewDate"></mat-datepicker-toggle>
<mat-datepicker #crewDate></mat-datepicker>
</mat-form-field>
For this reason, I did not need to override the parse()
function in DatePipeDateAdapter
, but if you do need to, check out some of the other examples for how to implement a custom parse()
function.