13

I'm working at an Angular9 app that works with dates. It displays and updates several <mat-datepicker> fields.

I am using a custom MAT_DATE_FORMATS and MomentDateAdapter (@angular/material-moment-adapter) from https://www.npmjs.com/package/@angular/material-moment-adapter.

I'm using the following code which works okay:

export const APP_MOMENT_DATE_FORMATS =
{    
  parse: {},
  display: {
    dateInput: 'YYYY/MM/DD/MM',
    monthYearLabel: 'MMM YYYY',
    dateA11yLabel: 'LL',
    monthYearA11yLabel: 'MMMM YYYY'
  }
};

//..
providers: [
  {
     provide: MAT_DATE_FORMATS, useValue: APP_MOMENT_DATE_FORMATS
  },
  {
    provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE]
  }
]
// ..

Stackblitz demo: https://stackblitz.com/edit/angular-formatted-datepicker-vdtaqj?file=app%2Fdatepicker-overview-example.ts

Now, I was wondering if I could update the value of the APP_MOMENT_DATE_FORMATS.display.dateInput dynamically - eg: maybe it will be setted from a configuration service via a http request, or maybe I will display a selector in the interface.

example:

export const APP_MOMENT_DATE_FORMATS =
  {  
    // ..  
    display: {
     dateInput: this.appService.dateInputFormat,
     // ..
   }
  };

I know that I could implement a custom date adapter, as described here: Angular 2 Material 2 datepicker date format.

But I was wondering if there is another method to do it, without implementing a new custom date adapter.

Thanks.

cuongle
  • 74,024
  • 28
  • 151
  • 206
andreivictor
  • 7,628
  • 3
  • 48
  • 75

2 Answers2

5

After spending hours looking for solution, I come up the below solution by just simply injecting MAT_DATE_FORMATS and change it dynamically:

const APP_MOMENT_DATE_FORMATS =
{
    parse: {
    dateInput: 'MM/DD/YYYY',
    },
    display: {
    dateInput: 'MM/DD/YYYY',
    monthYearLabel: 'MMM YYYY',
    dateA11yLabel: 'LL',
    monthYearA11yLabel: 'MMMM YYYY'
    }
};

@Component({
...  
providers: [
    {
    provide: MAT_DATE_FORMATS, useValue: APP_MOMENT_DATE_FORMATS
    },
    {
    provide: DateAdapter,
    useClass: MomentDateAdapter,
    deps: [MAT_DATE_LOCALE]
    }
]
})

export class DatePickerComponent implements OnInit, OnDestroy {

constructor(@Inject(MAT_DATE_FORMATS) public data: any) {
    data.parse.dateInput = 'DD/MM/YYYY'
    data.display.dateInput = 'DD/MM/YYYY'
  }
}
cuongle
  • 74,024
  • 28
  • 151
  • 206
0

Not too sure how to update the provider's value at runtime, but there is workaround to provide a different MAT_DATE_FORMATS value for mat-datepicker at component level.

As mentioned in Angular Docs:

Components and directives on the same element share an injector.

Place a customPickerFormats directive with a different MAT_DATE_FORMATS provider into the mat-form-field wrapping the mat-datepicker.

in directice.ts:

import { Directive } from '@angular/core';
import { MAT_DATE_FORMATS } from '@angular/material/core';

@Directive({
  selector: '[appCustomPickerFormats]',
  providers: [
    {
      provide: MAT_DATE_FORMATS,
      useValue: {
        parse: {
          dateInput: 'DD/MM',
        },
        display: {
          dateInput: 'Do MMMM',
          monthYearLabel: 'MMM',
          dateA11yLabel: 'LL',
          monthYearA11yLabel: 'MMMM YYYY',
        },
      },
    },
  ],
})
export class CustomPickerFormatsDirective {}

in component.ts:

<mat-form-field appCustomPickerFormats>
  <mat-label>Choose a date</mat-label>
  <input matInput [matDatepicker]="customPicker">
  <mat-datepicker-toggle matSuffix [for]="customPicker"></mat-datepicker-toggle>
  <mat-datepicker #customPicker></mat-datepicker>
</mat-form-field>

The first mat-datepicker without the directive will by default use whatever MAT_DATE_FORMATS value you set for the HostComponent or the modules the component lays.

enter image description here

A working stackblitz example here.

Neo Liu
  • 393
  • 5
  • 11