Skip to content

Instantly share code, notes, and snippets.

@alexnoise79
Last active March 12, 2019 16:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexnoise79/47e67ffe6d055c6821a9fa27fef1a8f4 to your computer and use it in GitHub Desktop.
Save alexnoise79/47e67ffe6d055c6821a9fa27fef1a8f4 to your computer and use it in GitHub Desktop.
Angular2 date-selector component, made of 3 options
date-selector {
@include flexbox((display: flex));
width: 100%;
select {
width: auto;
&.month {
@include flex(1 0 auto);
margin: 0 6px;
}
&.day,
&.year {
@include flex(0 1 auto);
}
}
&.ng-invalid.ng-touched {
background-color: transparent;
}
}
<select class="day" [(ngModel)]="date" (ngModelChange)="modelChanged($event, 'date')" [disabled]="isDisabled" (blur)="onBlur('date')" minlength="1" required>
<option [value]="null" *ngIf="!date">day</option>
<option [ngValue]="d" *ngFor="let d of dates">{{d}}</option>
</select>
<select class="month" [(ngModel)]="month" (ngModelChange)="modelChanged($event, 'month')" [disabled]="isDisabled" (blur)="onBlur('month')" required>
<option [value]="null" *ngIf="!month">month</option>
<option [ngValue]="z" *ngFor="let z of months">{{z.name}}</option>
</select>
<select class="year" [(ngModel)]="year" (ngModelChange)="modelChanged($event, 'year')" [disabled]="isDisabled" (blur)="onBlur('year')" minlength="4" required>
<option [value]="null" *ngIf="!year">year</option>
<option [ngValue]="y" *ngFor="let y of years">{{y}}</option>
</select>
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<number>;
months: Array<Month>;
years: Array<number>;
allMonths: Array<Month>;
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<number>();
this.years = new Array<number>();
this.months = new Array<Month>();
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<number> {
const years = new Array<number>();
for (let i = this.min.getFullYear(); i <= this.max.getFullYear(); i++) {
years.push(i);
}
return years;
}
private updateMonths(year?: number): Array<Month> {
let months = new Array<Month>();
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<number> {
let minDate, maxDate;
if (month) {
this.month = month;
}
const dates = new Array<number>();
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;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment