Created
November 14, 2017 18:56
-
-
Save mbenedettini/943aacf16ba338df7c20446e25551d44 to your computer and use it in GitHub Desktop.
Localized date adapter for Angular 4 Material DatePicker
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {Inject, Injectable, Optional} from '@angular/core'; | |
import {DateAdapter, MAT_DATE_LOCALE, MatDateFormats} from '@angular/material'; | |
import * as moment from 'moment'; | |
/** Creates an array and fills it with values. */ | |
function range<T>(length: number, valueFunction: (index: number) => T): T[] { | |
const valuesArray = Array(length); | |
for (let i = 0; i < length; i++) { | |
valuesArray[i] = valueFunction(i); | |
} | |
return valuesArray; | |
} | |
/** Uses Moment internally to provide locale support when it comes to formatting | |
and displaying days and month names, but its interface is based on Date. | |
Should work as a drop-in replacement for NativeDateAdapter. | |
Heavily based on MomentDateAdapter. | |
Usage: | |
// required imports | |
import { | |
LocaleDateAdapter, | |
MAT_LOCALE_DATE_FORMATS | |
} from './locale-date-adapter'; | |
import { | |
DateAdapter, | |
MAT_DATE_LOCALE | |
} from '@angular/material'; | |
// inject it into your app root module, | |
// useValue must be a valid moment locale, see | |
// https://github.com/moment/moment/tree/develop/src/locale | |
@NgModule({ | |
providers: [ | |
{provide: DateAdapter, useClass: MyDateAdapter}, | |
{provide: MAT_DATE_LOCALE, useValue: 'es'}, | |
{provide: MAT_DATE_FORMATS, useValue: MAT_LOCALE_DATE_FORMATS} | |
] | |
}) | |
export class MyComponent {} | |
**/ | |
@Injectable() | |
export class LocaleDateAdapter extends DateAdapter<Date> { | |
private _localeData: { | |
firstDayOfWeek: number, | |
longMonths: string[], | |
shortMonths: string[], | |
dates: string[], | |
longDaysOfWeek: string[], | |
shortDaysOfWeek: string[], | |
narrowDaysOfWeek: string[] | |
}; | |
constructor(@Optional() @Inject(MAT_DATE_LOCALE) dateLocale: string) { | |
super(); | |
this.setLocale(dateLocale || moment.locale()); | |
} | |
setLocale(locale: string) { | |
super.setLocale(locale); | |
let momentLocaleData = moment.localeData(locale); | |
this._localeData = { | |
firstDayOfWeek: momentLocaleData.firstDayOfWeek(), | |
longMonths: momentLocaleData.months(), | |
shortMonths: momentLocaleData.monthsShort(), | |
dates: range(31, (i) => this._momentFromDate( | |
this.createDate(2017, 0, i + 1) | |
).format('D')), | |
longDaysOfWeek: momentLocaleData.weekdays(), | |
shortDaysOfWeek: momentLocaleData.weekdaysShort(), | |
narrowDaysOfWeek: momentLocaleData.weekdaysMin(), | |
}; | |
} | |
getYear(date: Date): number { | |
return this._momentFromDate(date).year(); | |
} | |
getMonth(date: Date): number { | |
return this._momentFromDate(date).month(); | |
} | |
getDate(date: Date): number { | |
return this._momentFromDate(date).date(); | |
} | |
getDayOfWeek(date: Date): number { | |
return this._momentFromDate(date).day(); | |
} | |
getMonthNames(style: 'long' | 'short' | 'narrow'): string[] { | |
// Moment.js doesn't support narrow month names, so we just use short if narrow is requested. | |
return style == 'long' ? this._localeData.longMonths : this._localeData.shortMonths; | |
} | |
getDateNames(): string[] { | |
return this._localeData.dates; | |
} | |
getDayOfWeekNames(style: 'long' | 'short' | 'narrow'): string[] { | |
if (style == 'long') { | |
return this._localeData.longDaysOfWeek; | |
} | |
if (style == 'short') { | |
return this._localeData.shortDaysOfWeek; | |
} | |
return this._localeData.narrowDaysOfWeek; | |
} | |
getYearName(date: Date): string { | |
return this._momentFromDate(date).format('YYYY'); | |
} | |
getFirstDayOfWeek(): number { | |
return this._localeData.firstDayOfWeek; | |
} | |
getNumDaysInMonth(date: Date): number { | |
return this._momentFromDate(date).daysInMonth(); | |
} | |
clone(date: Date): Date { | |
return moment(date).toDate(); | |
} | |
_momentFromDate(date: Date): moment.Moment { | |
return moment(date).locale(this.locale); | |
} | |
createDate(year: number, month: number, date: number): Date { | |
// Moment.js will create an invalid date if any of the components are out of bounds, but we | |
// explicitly check each case so we can throw more descriptive errors. | |
if (month < 0 || month > 11) { | |
throw Error(`Invalid month index "${month}". Month index has to be between 0 and 11.`); | |
} | |
if (date < 1) { | |
throw Error(`Invalid date "${date}". Date has to be greater than 0.`); | |
} | |
let result = moment({year, month, date}).locale(this.locale); | |
// If the result isn't valid, the date must have been out of bounds for this month. | |
if (!result.isValid()) { | |
throw Error(`Invalid date "${date}" for month with index "${month}".`); | |
} | |
return result.toDate(); | |
} | |
today(): Date { | |
return moment().locale(this.locale).toDate(); | |
} | |
parse(value: any, parseFormat: string | string[]): Date | null { | |
if (value && typeof value == 'string') { | |
return moment(value, parseFormat, this.locale).toDate(); | |
} | |
return value ? moment(value).locale(this.locale).toDate() : null; | |
} | |
format(date: Date, displayFormat: string): string { | |
if (!this.isValid(date)) { | |
throw Error('LocaleDateAdapter: Cannot format invalid date.'); | |
} | |
return this._momentFromDate(date).format(displayFormat); | |
} | |
addCalendarYears(date: Date, years: number): Date { | |
return this._momentFromDate(date).add({years}).toDate(); | |
} | |
addCalendarMonths(date: Date, months: number): Date { | |
return this._momentFromDate(date).add({months}).toDate(); | |
} | |
addCalendarDays(date: Date, days: number): Date { | |
return this._momentFromDate(date).add({days}).toDate(); | |
} | |
toIso8601(date: Date): string { | |
return this._momentFromDate(date).format(); | |
} | |
isValid(date: Date): boolean { | |
return this._momentFromDate(date).isValid(); | |
} | |
isDateInstance(date: Date): boolean { | |
return this._momentFromDate(date).isValid(); | |
} | |
fromIso8601(date: string): Date { | |
return moment(date).locale(this.locale).toDate(); | |
} | |
} | |
export const MAT_LOCALE_DATE_FORMATS: MatDateFormats = { | |
parse: { | |
dateInput: 'l', | |
}, | |
display: { | |
dateInput: 'l', | |
monthYearLabel: 'MMM YYYY', | |
dateA11yLabel: 'LL', | |
monthYearA11yLabel: 'MMMM YYYY', | |
}, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment