How can I format the Angular Material datepicker without moment.js dependency
Asked Answered
S

4

14

What do I want to achieve?

I want my Angular Material(v11) datepicker to use the DD-MM-YYYY format in an Angular version 11 project.

What have I tried?

I tried using the MatMomentDateModule but this uses the moment.js library. This in turn will use all the locales in the bundle, which inceases the bundle size with 400kb. I have added CUSTOM_DATE_FORMATS to the providers of my app.module.ts which looks like this:

const CUSTOM_DATE_FORMATS = {
    parse: {
        dateInput: 'DD-MM-YYYY',
    },
    display: {
        dateInput: 'DD-MM-YYYY',
        monthYearLabel: 'MMMM YYYY',
        dateA11yLabel: 'LL',
        monthYearA11yLabel: 'MMMM YYYY',
    },
};

And this works but as mentioned before, the bundle size increases severely and i don't use any locales.

Is there a way to do format my date DD-MM-YYYY, without the use of the moment.js library?

or

Is the moment.js treeshakable in a way so that i only use the stuff that i need?

Sideburns answered 5/1, 2021 at 14:46 Comment(3)
If I remember correctly the NativeDateModule uses the same injection token, i.e. MAT_DATE_FORMATS. So you should be able to add custom provider for that without using MomentDateAdapter. Not sure about parsing though - I'll try to set up blitz in spare time and check.Atheism
oh really? i thought that was moment.js specific. would definitely appreciate a blitz!Sideburns
I stand corrected - must have used MomentDateAdapter way too much. github.com/angular/components/blob/master/src/material/core/… It only uses MAT_DATE_LOCALE, so the dirty way out would be to provide locale within the component and call it a day. Other dirty option would be to extend NativeDateAdapter and override the format / parse methods so that it doesn't use locale but uses your defined formatting instead. Best solution would be writing custom DateAdapter and using it instead. Sorry, perhaps someone comes up with a better solution.Atheism
S
15

So i didn't knew if you used the MatNativeDateModule that you could also implement a custom date adapter. I thought this was something that was specific to MatMomentDateModule.

Once I figured this out i could just overwrite the format function and format it manually likes so:

export class CustomDateAdapter extends NativeDateAdapter {
    format(date: Date, displayFormat: any): string {
        const days = date.getDate();
        const months = date.getMonth() + 1;
        const year = date.getFullYear();
        return days + '-' + months + '-' + year;
    }
}

And implement it like so:

@NgModule({

  providers: [
    {
      provide: DateAdapter,
      useClass: CustomDateAdapter,
      deps: [MAT_DATE_LOCALE, Platform]
    },
  ]

})
export class AppModule { }
Sideburns answered 6/1, 2021 at 13:10 Comment(6)
Hey Martijn, great solution and no moment dependency! do you know of a way to apply this approach to a single Datepicker? I am attempting to construct a month/year only datepicker and need to format the input accordingly.Mew
@SamScholefield use this provider just in component, where the datepicker is used. If you have more datepicker in this particular component, just create new component which is just consiting of that datepickerRafaelle
Great, thank you. But I would suggest choosing the ISO8601 format for better worldwide compatibility, (if it might be used worldwide): return ${year}-${months}-${days};Ventose
Hello, where did you use CustomDateAdapter ? what is Platform ? Thank you ?Inwardly
I cant figure out why its not working. I understand the code. But the mat-datepicker does not follow what is in the format() function.Whacky
@Whacky In my case it didn't work because a had MatNativeModule included in the module that uses the Datepicker. Since we are defining a new DateAdapter in the app.module.ts for all Datepickers in the application, that include is unnecesary.Pavior
A
2

Found this solution here and it works as intended https://www.angularjswiki.com/material/datepicker/#mat-datepicker-date-format

import { NativeDateAdapter, DateAdapter,MAT_DATE_FORMATS } from '@angular/material';
import { formatDate } from '@angular/common';

export const PICK_FORMATS = {
  parse: {dateInput: {month: 'short', year: 'numeric', day: 'numeric'}},
  display: {
      dateInput: 'input',
      monthYearLabel: {year: 'numeric', month: 'short'},
      dateA11yLabel: {year: 'numeric', month: 'long', day: 'numeric'},
      monthYearA11yLabel: {year: 'numeric', month: 'long'}
  }
};

class PickDateAdapter extends NativeDateAdapter {
  format(date: Date, displayFormat: Object): string {
      if (displayFormat === 'input') {
          return formatDate(date,'dd-MMM-yyyy',this.locale);;
      } else {
          return date.toDateString();
      }
  }
}

And then just use it in your module or component providers

providers: [
    {provide: DateAdapter, useClass: PickDateAdapter},
    {provide: MAT_DATE_FORMATS, useValue: PICK_FORMATS}
]
Arson answered 27/4, 2022 at 18:16 Comment(2)
Considerung the first line of the above proposal, I needed instead: import { NativeDateAdapter, DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';Teacart
... And if you need an ISO-date, simply return: return formatDate(date,'yyyy-MM-dd',this.locale); in class PickDateAdapter.Teacart
C
1

Had the same issue myself today and did not want to use moment.js. It seems if you only want a standard date format (e.g. non-US) a simple solution is to add to your app.module.ts:

import { MAT_DATE_LOCALE } from '@angular/material/core';
@NgModule({
...
  providers: [{provide: MAT_DATE_LOCALE, useValue: 'en-GB'}],
...

No doubt other country codes would also work. This approach presumably changes all date appearances across your application (not just your datepicker) but in my case this was perfectly acceptable. No further changes were required in the component nor HTML template.

Chessa answered 3/6, 2021 at 5:59 Comment(0)
B
1

Testing Martijn van den Bergh answer I got some problems. All dates are formatted the same, that includes some places like the month title that was displaying the provided format "01-06-2023" instead of "JUN 2023". So I came up with an evolution of it.

import { NativeDateAdapter } from '@angular/material/core';

export class CustomDateAdapter extends NativeDateAdapter {
  override format(
    date: Date,
    displayFormat: { year?: string; month?: string; day?: string }
  ): string {
    // Customize formatting in a full numeric date.
    if (
      displayFormat.day === 'numeric' &&
      displayFormat.month === 'numeric' &&
      displayFormat.year === 'numeric'
    ) {
      const day = date.getDate();
      const month = date.getMonth() + 1;
      const year = date.getFullYear();

      return (
        day.toString().padStart(2, '0') +
        '/' +
        month.toString().padStart(2, '0') +
        '/' +
        year
      );
    }

    // For the other cases, return the default
    return super.format(date, displayFormat);
  }

  // For returning Monday as the fist day of the week
  override getFirstDayOfWeek(): number {
    return 1;
  }
}

and then on the app:

@NgModule({
  providers: [
    {
      provide: DateAdapter,
      useClass: CustomDateAdapter,
      deps: [MAT_DATE_LOCALE]
    },
  ]
})
export class AppModule {}
Barquisimeto answered 15/6, 2023 at 15:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.