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 DateAdapterwhich extendsNativeDateAdapterand overrides the parentformat()function, but falls back on the parent viasuper.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 DateAdapterandMAT_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 insideAppModule.
@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.