Skip to content

Instantly share code, notes, and snippets.

@mbenedettini
Created November 14, 2017 18:56
Show Gist options
  • Save mbenedettini/943aacf16ba338df7c20446e25551d44 to your computer and use it in GitHub Desktop.
Save mbenedettini/943aacf16ba338df7c20446e25551d44 to your computer and use it in GitHub Desktop.
Localized date adapter for Angular 4 Material DatePicker
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