import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; interface Month { name: string; value: number; } @Component({ selector: 'date-selector', templateUrl: './date-selector.component.html', providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DateSelectorComponent), multi: true } ] }) export class DateSelectorComponent implements OnInit, ControlValueAccessor { dates: Array; months: Array; years: Array; allMonths: Array; year: number; month: Month; date: number; modelDate: Date; isDisabled: boolean; touched: any; @Input() min: Date; @Input() max: Date; constructor(translate: TranslateService) { this.dates = new Array(); this.years = new Array(); this.months = new Array(); this.touched = {}; this.allMonths = this.getMonthsName(translate.getBrowserCultureLang(), 'long'); } writeValue(model: Date): void { if (model) { if (model && model < this.min || model > this.max) { throw new Error('Provided date is not in range'); } this.modelDate = new Date(model); if (this.modelDate) { this.date = this.modelDate.getDate(); this.month = this.months.find(month => month.value === this.modelDate.getMonth() + 1); this.year = this.modelDate.getFullYear(); } else { this.date = this.month = this.year = null; } this.months = this.updateMonths(this.modelDate.getFullYear()); } else { this.date = this.month = this.year = null; } } registerOnChange(fn: any): void { this._onChange = fn; } registerOnTouched(fn: any): void { this._onTouched = fn; } setDisabledState(isDisabled: boolean): void { this.isDisabled = isDisabled; } ngOnInit() { this.min = this.min ? new Date(this.min) : this.defaults(true); this.max = this.max ? new Date(this.max) : this.defaults(false); this.dates = this.updateDates(); this.months = this.updateMonths(); this.years = this.updateYears(); } onBlur(which: string) { this.touched[which] = true; if (this.touched.date && this.touched.month && this.touched.year) { this._onTouched(); } } modelChanged(event: any, select: string) { switch (select) { case 'year': this.months = this.updateMonths(event); break; case 'month': this.dates = this.updateDates(event); break; case 'date': this.updateModel(); break; } } private getDaysInMonth = function (year, month): number { return [31, (this.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; }; private _onChange(_model: string) { } private _onTouched() { } private pad(n) { return n < 10 ? '0' + n : n; } private updateModel() { if (this.year && this.month && this.date) { const ISODate = this.pad(this.year) + '-' + this.pad(this.month.value) + '-' + this.pad(this.date); this._onChange(new Date(ISODate).toISOString()); } } private isLeapYear(year): boolean { return ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0); } private defaults(isMin: boolean): Date { const m = new Date(); const y = isMin ? m.getFullYear() - 100 : m.getFullYear() + 50; m.setFullYear(y); return m; } private updateYears(): Array { const years = new Array(); for (let i = this.min.getFullYear(); i <= this.max.getFullYear(); i++) { years.push(i); } return years; } private updateMonths(year?: number): Array { let months = new Array(); if (year === this.min.getFullYear() || year === this.max.getFullYear()) { const minMonth = year && (this.min.getFullYear() === year) ? this.min.getMonth() : 0; const maxMonth = year && (this.max.getFullYear() === year) ? this.max.getMonth() : 11; for (let j = minMonth; j <= maxMonth; j++) { months.push(this.allMonths[j]); } const sel = this.modelDate.getMonth() ? months.find(m => m.value === this.modelDate.getMonth() + 1) : months[0]; this.dates = this.updateDates(sel); } else { this.dates = this.updateDates(this.month); months = this.allMonths; } return months; } private getMonthsName(locale, size) { const format = new Intl.DateTimeFormat(locale, {month: size}); const months = []; for (let month = 0; month < 12; month++) { const testDate = new Date(Date.UTC(2000, month, 1, 0, 0, 0)); months.push({name: format.format(testDate), value: month + 1}); } return months; } private updateDates(month?: Month): Array { let minDate, maxDate; if (month) { this.month = month; } const dates = new Array(); if (this.year && month && (this.min.getFullYear() === this.year && this.min.getMonth() === month.value - 1)) { minDate = this.min.getDate(); } else { minDate = 1; } if (this.year && month && (this.max.getFullYear() === this.year && this.max.getMonth() === month.value - 1)) { maxDate = this.max.getDate(); } else if (this.year && month) { maxDate = this.getDaysInMonth(this.year, month.value - 1); } else { maxDate = 31; } for (let i = minDate; i <= maxDate; i++) { dates.push(i); } if (this.date > maxDate || this.date < minDate) { this.date = 1; } this.updateModel(); return dates; } }