From 7b43295000594ea51fb1b4d11ede301cc2adc1a1 Mon Sep 17 00:00:00 2001 From: Dmitriy Shekhovtsov Date: Fri, 19 Jan 2018 19:26:01 +0200 Subject: [PATCH] feat(datepicker): added date and range min, max and invalid validation (#3499) closes #2727 closes #3498 --- .../demos/min-max/min-max.component.html | 14 ++++- .../bs-datepicker-input.directive.ts | 51 +++++++++++++++--- .../bs-daterangepicker-input.directive.ts | 53 ++++++++++++++++--- 3 files changed, 104 insertions(+), 14 deletions(-) diff --git a/demo/src/app/components/+datepicker/demos/min-max/min-max.component.html b/demo/src/app/components/+datepicker/demos/min-max/min-max.component.html index 37f0721d2c..1c20072154 100644 --- a/demo/src/app/components/+datepicker/demos/min-max/min-max.component.html +++ b/demo/src/app/components/+datepicker/demos/min-max/min-max.component.html @@ -1,9 +1,19 @@
- +
- +
diff --git a/src/datepicker/bs-datepicker-input.directive.ts b/src/datepicker/bs-datepicker-input.directive.ts index 15b0e968a1..e66b9201cb 100644 --- a/src/datepicker/bs-datepicker-input.directive.ts +++ b/src/datepicker/bs-datepicker-input.directive.ts @@ -1,11 +1,16 @@ import { ChangeDetectorRef, Directive, ElementRef, forwardRef, Host, Renderer2 } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { BsDatepickerDirective } from './bs-datepicker.component'; +import { + AbstractControl, ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, + Validator +} from '@angular/forms'; +import { parseDate } from '../chronos/create/local'; import { formatDate } from '../chronos/format'; import { getLocale } from '../chronos/locale/locales'; -import { BsLocaleService } from './bs-locale.service'; +import { isAfter, isBefore } from '../chronos/utils/date-compare'; +import { isDate, isDateValid } from '../chronos/utils/type-checks'; +import { BsDatepickerDirective } from './bs-datepicker.component'; import { BsDatepickerConfig } from './bs-datepicker.config'; -import { parseDate } from '../chronos/create/local'; +import { BsLocaleService } from './bs-locale.service'; const BS_DATEPICKER_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, @@ -14,6 +19,12 @@ const BS_DATEPICKER_VALUE_ACCESSOR = { multi: true }; +const BS_DATEPICKER_VALIDATOR = { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => BsDatepickerInputDirective), + multi: true +}; + @Directive({ selector: `input[bsDatepicker]`, host: { @@ -21,12 +32,13 @@ const BS_DATEPICKER_VALUE_ACCESSOR = { '(keyup.esc)': 'hide()', '(blur)': 'onBlur()' }, - providers: [BS_DATEPICKER_VALUE_ACCESSOR] + providers: [BS_DATEPICKER_VALUE_ACCESSOR, BS_DATEPICKER_VALIDATOR] }) export class BsDatepickerInputDirective - implements ControlValueAccessor { + implements ControlValueAccessor, Validator { private _onChange = Function.prototype; private _onTouched = Function.prototype; + private _validatorChange = Function.prototype; private _value: Date; constructor(@Host() private _picker: BsDatepickerDirective, @@ -65,6 +77,33 @@ export class BsDatepickerInputDirective this._onTouched(); } + validate(c: AbstractControl): ValidationErrors | null { + const _value: Date | string = c.value; + + if (_value === null || _value === undefined || _value === '') { + return null; + } + + if (isDate(_value)) { + const _isDateValid = isDateValid(_value); + if (!_isDateValid) { + return { bsDate: { invalid: _value } }; + } + + if (this._picker && this._picker.minDate && isBefore(_value, this._picker.minDate, 'date')) { + return { bsDate: { minDate: this._picker.minDate } }; + } + + if (this._picker && this._picker.maxDate && isAfter(_value, this._picker.maxDate, 'date')) { + return { bsDate: { maxDate: this._picker.maxDate } }; + } + } + } + + registerOnValidatorChange(fn: () => void): void { + this._validatorChange = fn; + } + writeValue(value: Date | string) { if (!value) { this._value = null; diff --git a/src/datepicker/bs-daterangepicker-input.directive.ts b/src/datepicker/bs-daterangepicker-input.directive.ts index dc72f091f4..1133f3e58b 100644 --- a/src/datepicker/bs-daterangepicker-input.directive.ts +++ b/src/datepicker/bs-daterangepicker-input.directive.ts @@ -1,11 +1,16 @@ -import { ChangeDetectorRef, Directive, ElementRef, forwardRef, Host, OnInit, Renderer2 } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ChangeDetectorRef, Directive, ElementRef, forwardRef, Host, Renderer2 } from '@angular/core'; +import { + AbstractControl, ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, + Validator +} from '@angular/forms'; +import { parseDate } from '../chronos/create/local'; import { formatDate } from '../chronos/format'; import { getLocale } from '../chronos/locale/locales'; +import { isAfter, isBefore } from '../chronos/utils/date-compare'; +import { isArray, isDateValid } from '../chronos/utils/type-checks'; +import { BsDatepickerConfig } from './bs-datepicker.config'; import { BsDaterangepickerDirective } from './bs-daterangepicker.component'; import { BsLocaleService } from './bs-locale.service'; -import { BsDatepickerConfig } from './bs-datepicker.config'; -import { parseDate } from '../chronos/create/local'; const BS_DATERANGEPICKER_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, @@ -14,6 +19,14 @@ const BS_DATERANGEPICKER_VALUE_ACCESSOR = { multi: true }; + +const BS_DATERANGEPICKER_VALIDATOR = { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => BsDaterangepickerInputDirective), + multi: true +}; + + @Directive({ selector: `input[bsDaterangepicker]`, host: { @@ -21,12 +34,13 @@ const BS_DATERANGEPICKER_VALUE_ACCESSOR = { '(keyup.esc)': 'hide()', '(blur)': 'onBlur()' }, - providers: [BS_DATERANGEPICKER_VALUE_ACCESSOR] + providers: [BS_DATERANGEPICKER_VALUE_ACCESSOR, BS_DATERANGEPICKER_VALIDATOR] }) export class BsDaterangepickerInputDirective - implements ControlValueAccessor { + implements ControlValueAccessor, Validator { private _onChange = Function.prototype; private _onTouched = Function.prototype; + private _validatorChange = Function.prototype; private _value: Date[]; constructor(@Host() private _picker: BsDaterangepickerDirective, @@ -77,6 +91,33 @@ export class BsDaterangepickerInputDirective this._onTouched(); } + validate(c: AbstractControl): ValidationErrors | null { + const _value: [Date, Date] = c.value; + + if (_value === null || _value === undefined || !isArray(_value)) { + return null; + } + + + const _isDateValid = isDateValid(_value[0]) && isDateValid(_value[0]); + + if (!_isDateValid) { + return { bsDate: { invalid: _value } }; + } + + if (this._picker && this._picker.minDate && isBefore(_value[0], this._picker.minDate, 'date')) { + return { bsDate: { minDate: this._picker.minDate } }; + } + + if (this._picker && this._picker.maxDate && isAfter(_value[1], this._picker.maxDate, 'date')) { + return { bsDate: { maxDate: this._picker.maxDate } }; + } + } + + registerOnValidatorChange(fn: () => void): void { + this._validatorChange = fn; + } + writeValue(value: Date[] | string) { if (!value) { this._value = null;