[(ngModel)]="date"
[min]="minDate"
[max]="maxDate"
+ [mdDatepickerFilter]="filterOdd ? dateFilter : null"
placeholder="Pick a date">
+ [startView]="yearView ? 'year' : 'month'">
diff --git a/src/demo-app/datepicker/datepicker-demo.ts b/src/demo-app/datepicker/datepicker-demo.ts
index 7a70a9c0b4bb..fe558123a8d8 100644
--- a/src/demo-app/datepicker/datepicker-demo.ts
+++ b/src/demo-app/datepicker/datepicker-demo.ts
@@ -15,5 +15,5 @@ export class DatepickerDemo {
maxDate: Date;
startAt: Date;
date: Date;
- dateFilter = (date: Date) => date.getMonth() % 2 == 0 && date.getDate() % 2 == 0;
+ dateFilter = (date: Date) => !date || date.getMonth() % 2 == 1 && date.getDate() % 2 == 0;
}
diff --git a/src/lib/core/datetime/date-adapter.ts b/src/lib/core/datetime/date-adapter.ts
index 33148d3aa714..938d318339af 100644
--- a/src/lib/core/datetime/date-adapter.ts
+++ b/src/lib/core/datetime/date-adapter.ts
@@ -140,6 +140,14 @@ export abstract class DateAdapter {
*/
abstract addCalendarDays(date: D, days: number): D;
+ /**
+ * Gets the RFC 3339 compatible date string (https://tools.ietf.org/html/rfc3339) for the given
+ * date.
+ * @param date The date to get the ISO date string for.
+ * @returns The ISO date string date string.
+ */
+ abstract getISODateString(date: D): string;
+
/**
* Sets the locale used for all dates.
* @param locale The new locale.
diff --git a/src/lib/core/datetime/native-date-adapter.ts b/src/lib/core/datetime/native-date-adapter.ts
index 7ea5c8e4efac..9e952076eb86 100644
--- a/src/lib/core/datetime/native-date-adapter.ts
+++ b/src/lib/core/datetime/native-date-adapter.ts
@@ -160,6 +160,14 @@ export class NativeDateAdapter extends DateAdapter {
this.getYear(date), this.getMonth(date), this.getDate(date) + days);
}
+ getISODateString(date: Date): string {
+ return [
+ date.getUTCFullYear(),
+ this._2digit(date.getUTCMonth() + 1),
+ this._2digit(date.getUTCDate())
+ ].join('-');
+ }
+
/** Creates a date but allows the month and date to overflow. */
private _createDateWithOverflow(year: number, month: number, date: number) {
let result = new Date(year, month, date);
@@ -171,4 +179,13 @@ export class NativeDateAdapter extends DateAdapter {
}
return result;
}
+
+ /**
+ * Pads a number to make it two digits.
+ * @param n The number to pad.
+ * @returns The padded number.
+ */
+ private _2digit(n: number) {
+ return ('00' + n).slice(-2);
+ }
}
diff --git a/src/lib/datepicker/datepicker-content.html b/src/lib/datepicker/datepicker-content.html
index dd8a696741cb..0f3a70ec7dff 100644
--- a/src/lib/datepicker/datepicker-content.html
+++ b/src/lib/datepicker/datepicker-content.html
@@ -4,7 +4,7 @@
[startView]="datepicker.startView"
[minDate]="datepicker._minDate"
[maxDate]="datepicker._maxDate"
- [dateFilter]="datepicker.dateFilter"
+ [dateFilter]="datepicker._dateFilter"
[selected]="datepicker._selected"
(selectedChange)="datepicker._selectAndClose($event)">
diff --git a/src/lib/datepicker/datepicker-input.ts b/src/lib/datepicker/datepicker-input.ts
index 7767fcae98ef..daa84a37ad39 100644
--- a/src/lib/datepicker/datepicker-input.ts
+++ b/src/lib/datepicker/datepicker-input.ts
@@ -11,7 +11,15 @@ import {
Renderer
} from '@angular/core';
import {MdDatepicker} from './datepicker';
-import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
+import {
+ AbstractControl,
+ ControlValueAccessor,
+ NG_VALIDATORS,
+ NG_VALUE_ACCESSOR,
+ ValidationErrors,
+ Validator,
+ ValidatorFn
+} from '@angular/forms';
import {Subscription} from 'rxjs/Subscription';
import {MdInputContainer} from '../input/input-container';
import {DOWN_ARROW} from '../core/keyboard/keycodes';
@@ -27,22 +35,38 @@ export const MD_DATEPICKER_VALUE_ACCESSOR: any = {
};
+export const MD_DATEPICKER_VALIDATORS: any = {
+ provide: NG_VALIDATORS,
+ useExisting: forwardRef(() => MdDatepickerInput),
+ multi: true
+};
+
+
+/** Validator that requires dates to match a filter function. */
+export function mdDatepickerFilterValidator(filter: (date: D | null) => boolean): ValidatorFn {
+ return (control: AbstractControl): ValidationErrors | null => {
+ return !filter(control.value) ? {'mdDatepickerFilter': true} : null;
+ }
+}
+
+
/** Directive used to connect an input to a MdDatepicker. */
@Directive({
selector: 'input[mdDatepicker], input[matDatepicker]',
- providers: [MD_DATEPICKER_VALUE_ACCESSOR],
+ providers: [MD_DATEPICKER_VALUE_ACCESSOR, MD_DATEPICKER_VALIDATORS],
host: {
'[attr.aria-expanded]': '_datepicker?.opened || "false"',
'[attr.aria-haspopup]': 'true',
'[attr.aria-owns]': '_datepicker?.id',
- '[min]': '_min',
- '[max]': '_max',
+ '[attr.min]': 'min ? _dateAdapter.getISODateString(min) : null',
+ '[attr.max]': 'max ? _dateAdapter.getISODateString(max) : null',
'(input)': '_onInput($event.target.value)',
'(blur)': '_onTouched()',
'(keydown)': '_onKeydown($event)',
}
})
-export class MdDatepickerInput implements AfterContentInit, ControlValueAccessor, OnDestroy {
+export class MdDatepickerInput implements AfterContentInit, ControlValueAccessor, OnDestroy,
+ Validator {
/** The datepicker that this input is associated with. */
@Input()
set mdDatepicker(value: MdDatepicker) {
@@ -53,8 +77,20 @@ export class MdDatepickerInput implements AfterContentInit, ControlValueAcces
}
_datepicker: MdDatepicker;
- @Input()
- set matDatepicker(value: MdDatepicker) { this.mdDatepicker = value; }
+ @Input() set matDatepicker(value: MdDatepicker) { this.mdDatepicker = value; }
+
+ @Input() set mdDatepickerFilter(filter: (date: D | null) => boolean) {
+ this._dateFilter = filter;
+ this._validator = filter ? mdDatepickerFilterValidator(filter) : null;
+ if (this._validatorOnChange) {
+ this._validatorOnChange();
+ }
+ }
+ _dateFilter: (date: D | null) => boolean;
+
+ @Input() set matDatepickerFilter(filter: (date: D | null) => boolean) {
+ this.mdDatepickerFilter = filter;
+ }
/** The value of the input. */
@Input()
@@ -81,10 +117,14 @@ export class MdDatepickerInput implements AfterContentInit, ControlValueAcces
/** Emits when the value changes (either due to user input or programmatic change). */
_valueChange = new EventEmitter();
- _onChange = (value: any) => {};
-
_onTouched = () => {};
+ private _cvaOnChange = (value: any) => {};
+
+ private _validator: ValidatorFn;
+
+ private _validatorOnChange: () => void;
+
private _datepickerSubscription: Subscription;
constructor(
@@ -106,7 +146,7 @@ export class MdDatepickerInput implements AfterContentInit, ControlValueAcces
this._datepickerSubscription =
this._datepicker.selectedChanged.subscribe((selected: D) => {
this.value = selected;
- this._onChange(selected);
+ this._cvaOnChange(selected);
});
}
}
@@ -117,6 +157,14 @@ export class MdDatepickerInput implements AfterContentInit, ControlValueAcces
}
}
+ registerOnValidatorChange(fn: () => void): void {
+ this._validatorOnChange = fn;
+ }
+
+ validate(c: AbstractControl): ValidationErrors | null {
+ return this._validator ? this._validator(c) : null;
+ }
+
/**
* Gets the element that the datepicker popup should be connected to.
* @return The element to connect the popup to.
@@ -132,7 +180,7 @@ export class MdDatepickerInput implements AfterContentInit, ControlValueAcces
// Implemented as part of ControlValueAccessor
registerOnChange(fn: (value: any) => void): void {
- this._onChange = fn;
+ this._cvaOnChange = fn;
}
// Implemented as part of ControlValueAccessor
@@ -154,7 +202,7 @@ export class MdDatepickerInput implements AfterContentInit, ControlValueAcces
_onInput(value: string) {
let date = this._dateAdapter.parse(value, this._dateFormats.parse.dateInput);
- this._onChange(date);
+ this._cvaOnChange(date);
this._valueChange.emit(date);
}
}
diff --git a/src/lib/datepicker/datepicker.spec.ts b/src/lib/datepicker/datepicker.spec.ts
index f92e76672fa9..bbe8858522b7 100644
--- a/src/lib/datepicker/datepicker.spec.ts
+++ b/src/lib/datepicker/datepicker.spec.ts
@@ -17,7 +17,7 @@ const JAN = 0, FEB = 1, MAR = 2, APR = 3, MAY = 4, JUN = 5, JUL = 6, AUG = 7, SE
NOV = 10, DEC = 11;
-describe('MdDatepicker', () => {
+fdescribe('MdDatepicker', () => {
describe('with MdNativeDateModule', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
@@ -30,6 +30,7 @@ describe('MdDatepicker', () => {
ReactiveFormsModule,
],
declarations: [
+ DatepickerWithFilterAndValidation,
DatepickerWithFormControl,
DatepickerWithMinAndMax,
DatepickerWithNgModel,
@@ -428,6 +429,58 @@ describe('MdDatepicker', () => {
expect(testComponent.datepicker._maxDate).toEqual(new Date(2020, JAN, 1));
});
});
+
+ describe('datepicker with filter and validation', () => {
+ let fixture: ComponentFixture;
+ let testComponent: DatepickerWithFilterAndValidation;
+
+ beforeEach(async(() => {
+ fixture = TestBed.createComponent(DatepickerWithFilterAndValidation);
+ fixture.detectChanges();
+
+ testComponent = fixture.componentInstance;
+ }));
+
+ afterEach(async(() => {
+ testComponent.datepicker.close();
+ fixture.detectChanges();
+ }));
+
+ it('should mark input invalid', async(() => {
+ testComponent.date = new Date(2017, JAN, 1);
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+
+ expect(fixture.debugElement.query(By.css('input')).nativeElement.classList)
+ .toContain('ng-invalid');
+
+ testComponent.date = new Date(2017, JAN, 2);
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+
+ expect(fixture.debugElement.query(By.css('input')).nativeElement.classList)
+ .not.toContain('ng-invalid');
+ });
+ });
+ }));
+
+ it('should disable filtered calendar cells', () => {
+ fixture.detectChanges();
+
+ testComponent.datepicker.open();
+ fixture.detectChanges();
+
+ expect(document.querySelector('md-dialog-container')).not.toBeNull();
+
+ let cells = document.querySelectorAll('.mat-calendar-body-cell');
+ expect(cells[0].classList).toContain('mat-calendar-body-disabled');
+ expect(cells[1].classList).not.toContain('mat-calendar-body-disabled');
+ });
+ });
});
describe('with missing DateAdapter and MD_DATE_FORMATS', () => {
@@ -440,17 +493,7 @@ describe('MdDatepicker', () => {
NoopAnimationsModule,
ReactiveFormsModule,
],
- declarations: [
- DatepickerWithFormControl,
- DatepickerWithMinAndMax,
- DatepickerWithNgModel,
- DatepickerWithStartAt,
- DatepickerWithToggle,
- InputContainerDatepicker,
- MultiInputDatepicker,
- NoInputDatepicker,
- StandardDatepicker,
- ],
+ declarations: [StandardDatepicker],
});
TestBed.compileComponents();
@@ -567,3 +610,17 @@ class DatepickerWithMinAndMax {
maxDate = new Date(2020, JAN, 1);
@ViewChild('d') datepicker: MdDatepicker;
}
+
+
+@Component({
+ template: `
+
+
+
+ `,
+})
+class DatepickerWithFilterAndValidation {
+ @ViewChild('d') datepicker: MdDatepicker;
+ date: Date;
+ filter = (date: Date) => !date || date.getDate() != 1;
+}
diff --git a/src/lib/datepicker/datepicker.ts b/src/lib/datepicker/datepicker.ts
index 17f8945e2c44..4ba113254244 100644
--- a/src/lib/datepicker/datepicker.ts
+++ b/src/lib/datepicker/datepicker.ts
@@ -116,9 +116,6 @@ export class MdDatepicker implements OnDestroy {
*/
@Input() touchUi = false;
- /** A function used to filter which dates are selectable. */
- @Input() dateFilter: (date: D) => boolean;
-
/** Emits new selected date when selected date changes. */
@Output() selectedChanged = new EventEmitter();
@@ -141,6 +138,10 @@ export class MdDatepicker implements OnDestroy {
return this._datepickerInput && this._datepickerInput.max;
}
+ get _dateFilter(): (date: D | null) => boolean {
+ return this._datepickerInput && this._datepickerInput._dateFilter;
+ }
+
/** A reference to the overlay when the calendar is opened as a popup. */
private _popupRef: OverlayRef;
From 09e2b9347dfd2cb7dfc915d985de833a35f77732 Mon Sep 17 00:00:00 2001
From: Miles Malerba
Date: Thu, 4 May 2017 15:43:40 -0700
Subject: [PATCH 2/5] add validation support for min & max
---
src/demo-app/datepicker/datepicker-demo.html | 4 ++
src/demo-app/datepicker/datepicker-demo.ts | 2 +-
src/lib/datepicker/datepicker-input.ts | 53 +++++++++++++-------
src/lib/datepicker/datepicker.spec.ts | 4 +-
4 files changed, 42 insertions(+), 21 deletions(-)
diff --git a/src/demo-app/datepicker/datepicker-demo.html b/src/demo-app/datepicker/datepicker-demo.html
index 1b721a32abab..a9f62c5ea86d 100644
--- a/src/demo-app/datepicker/datepicker-demo.html
+++ b/src/demo-app/datepicker/datepicker-demo.html
@@ -30,12 +30,16 @@
Result
+ Too early!
+ Too late!
+ Date unavailable! !date || date.getMonth() % 2 == 1 && date.getDate() % 2 == 0;
+ dateFilter = (date: Date) => date.getMonth() % 2 == 1 && date.getDate() % 2 == 0;
}
diff --git a/src/lib/datepicker/datepicker-input.ts b/src/lib/datepicker/datepicker-input.ts
index daa84a37ad39..dde9a3c9ac82 100644
--- a/src/lib/datepicker/datepicker-input.ts
+++ b/src/lib/datepicker/datepicker-input.ts
@@ -18,7 +18,7 @@ import {
NG_VALUE_ACCESSOR,
ValidationErrors,
Validator,
- ValidatorFn
+ ValidatorFn, Validators
} from '@angular/forms';
import {Subscription} from 'rxjs/Subscription';
import {MdInputContainer} from '../input/input-container';
@@ -42,14 +42,6 @@ export const MD_DATEPICKER_VALIDATORS: any = {
};
-/** Validator that requires dates to match a filter function. */
-export function mdDatepickerFilterValidator(filter: (date: D | null) => boolean): ValidatorFn {
- return (control: AbstractControl): ValidationErrors | null => {
- return !filter(control.value) ? {'mdDatepickerFilter': true} : null;
- }
-}
-
-
/** Directive used to connect an input to a MdDatepicker. */
@Directive({
selector: 'input[mdDatepicker], input[matDatepicker]',
@@ -81,10 +73,7 @@ export class MdDatepickerInput implements AfterContentInit, ControlValueAcces
@Input() set mdDatepickerFilter(filter: (date: D | null) => boolean) {
this._dateFilter = filter;
- this._validator = filter ? mdDatepickerFilterValidator(filter) : null;
- if (this._validatorOnChange) {
- this._validatorOnChange();
- }
+ this._validatorOnChange();
}
_dateFilter: (date: D | null) => boolean;
@@ -109,10 +98,22 @@ export class MdDatepickerInput implements AfterContentInit, ControlValueAcces
}
/** The minimum valid date. */
- @Input() min: D;
+ @Input()
+ get min(): D { return this._min; }
+ set min(value: D) {
+ this._min = value;
+ this._validatorOnChange();
+ }
+ private _min: D;
/** The maximum valid date. */
- @Input() max: D;
+ @Input()
+ get max(): D { return this._max; }
+ set max(value: D) {
+ this._max = value;
+ this._validatorOnChange();
+ }
+ private _max: D;
/** Emits when the value changes (either due to user input or programmatic change). */
_valueChange = new EventEmitter();
@@ -121,12 +122,28 @@ export class MdDatepickerInput implements AfterContentInit, ControlValueAcces
private _cvaOnChange = (value: any) => {};
- private _validator: ValidatorFn;
-
- private _validatorOnChange: () => void;
+ private _validatorOnChange = () => {};
private _datepickerSubscription: Subscription;
+ /** The form control validator for this input. */
+ private _validator: ValidatorFn = Validators.compose([
+ (control: AbstractControl): ValidationErrors | null => {
+ return (!this.min || !control.value ||
+ this._dateAdapter.compareDate(this.min, control.value) < 0) ?
+ null : {'mdDatepickerMin': {'min': this.min, 'actual': control.value}};
+ },
+ (control: AbstractControl): ValidationErrors | null => {
+ return (!this.max || !control.value ||
+ this._dateAdapter.compareDate(this.max, control.value) > 0) ?
+ null : {'mdDatepickerMax': {'max': this.max, 'actual': control.value}};
+ },
+ (control: AbstractControl): ValidationErrors | null => {
+ return !this._dateFilter || !control.value || this._dateFilter(control.value) ?
+ null : {'mdDatepickerFilter': true};
+ },
+ ]);
+
constructor(
private _elementRef: ElementRef,
private _renderer: Renderer,
diff --git a/src/lib/datepicker/datepicker.spec.ts b/src/lib/datepicker/datepicker.spec.ts
index bbe8858522b7..19d91acfbc2e 100644
--- a/src/lib/datepicker/datepicker.spec.ts
+++ b/src/lib/datepicker/datepicker.spec.ts
@@ -17,7 +17,7 @@ const JAN = 0, FEB = 1, MAR = 2, APR = 3, MAY = 4, JUN = 5, JUL = 6, AUG = 7, SE
NOV = 10, DEC = 11;
-fdescribe('MdDatepicker', () => {
+describe('MdDatepicker', () => {
describe('with MdNativeDateModule', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
@@ -622,5 +622,5 @@ class DatepickerWithMinAndMax {
class DatepickerWithFilterAndValidation {
@ViewChild('d') datepicker: MdDatepicker;
date: Date;
- filter = (date: Date) => !date || date.getDate() != 1;
+ filter = (date: Date) => date.getDate() != 1;
}
From 7003f6505d2e748ace29f7605d82e4755c183420 Mon Sep 17 00:00:00 2001
From: Miles Malerba
Date: Thu, 4 May 2017 16:24:36 -0700
Subject: [PATCH 3/5] update docs to talk about validation
---
src/lib/datepicker/datepicker.md | 36 +++++++++++++++++++-------------
1 file changed, 22 insertions(+), 14 deletions(-)
diff --git a/src/lib/datepicker/datepicker.md b/src/lib/datepicker/datepicker.md
index 56278e9931b6..7f977bb39bb8 100644
--- a/src/lib/datepicker/datepicker.md
+++ b/src/lib/datepicker/datepicker.md
@@ -60,20 +60,22 @@ startDate = new Date(1990, 0, 1);
```
-### Preventing selection of specific dates
-There are two ways to restrict the dates available for selection in the datepicker. The first is by
-using the `min` and `max` properties of the input. This will disable all dates on the calendar
-before or after the respective given dates. It will also prevent the user from advancing the
-calendar past the `month` or `year` (depending on current view) containing the `min` or `max` date.
+### Date validation
+There are three properties that add date validation to the datepicker input. The first two are the
+`min` and `max` properties. In addition to enforcing validation on the input, these properties will
+disable all dates on the calendar popup before or after the respective values and prevent the user
+from advancing the calendar past the `month` or `year` (depending on current view) containing the
+`min` or `max` date.
-The second way to restrict selection is using the `dateFilter` property of `md-datepicker`. The
-`dateFilter` property accepts a function of ` => boolean` (where `` is the date type used by
+The second way to add date validation is using the `mdDatepickerFilter` property of the datepicker
+input. This property accepts a function of ` => boolean` (where `` is the date type used by
the datepicker, see section on
[choosing a date implementation](#choosing-a-date-implementation-and-date-format-settings)).
-A result of `true` indicates that the date is selectable and a result of `false` indicates that it
-is not. One important difference between using `dateFilter` vs using `min` or `max` is that
-filtering out all dates before a certain point, will not prevent the user from advancing the
-calendar past that point.
+A result of `true` indicates that the date is valid and a result of `false` indicates that it is
+not. Again this will also disable the dates on the calendar that are invalid. However, one important
+difference between using `mdDatepcikerFilter` vs using `min` or `max` is that filtering out all
+dates before or after a certain point, will not prevent the user from advancing the calendar past
+that point.
```ts
myFilter = (d: Date) => d.getFullYear() > 2005
@@ -82,12 +84,18 @@ maxDate = new Date(2020, 11, 31);
```
```html
-
-
+
+
```
In this example the user can back past 2005, but all of the dates before then will be unselectable.
-They will not be able to go further back in the calendar than 2000.
+They will not be able to go further back in the calendar than 2000. If they manually type in a date
+that is before the min, after the max, or filtered out the input will have validation errors.
+
+Each validation property has a different error that can be checked:
+ * A value that violates the `min` property will have a `mdDatepickerMin` error.
+ * A value that violates the `max` property will have a `mdDatepickerMax` error.
+ * A value that violates the `mdDatepickerFilter` property will have a `mdDatepickerFitler` error.
### Touch UI mode
The datepicker normally opens as a popup under the input. However this is not ideal for touch
From aeb9c66e64ffbac5dc00925f600b27eaa6ccd743 Mon Sep 17 00:00:00 2001
From: Miles Malerba
Date: Thu, 4 May 2017 16:45:18 -0700
Subject: [PATCH 4/5] fix imports
---
src/lib/datepicker/datepicker-input.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/lib/datepicker/datepicker-input.ts b/src/lib/datepicker/datepicker-input.ts
index dde9a3c9ac82..aac0acdae088 100644
--- a/src/lib/datepicker/datepicker-input.ts
+++ b/src/lib/datepicker/datepicker-input.ts
@@ -18,7 +18,8 @@ import {
NG_VALUE_ACCESSOR,
ValidationErrors,
Validator,
- ValidatorFn, Validators
+ ValidatorFn,
+ Validators
} from '@angular/forms';
import {Subscription} from 'rxjs/Subscription';
import {MdInputContainer} from '../input/input-container';
From 961746fafc23e13bbe397522ec53ab81fb44e5e3 Mon Sep 17 00:00:00 2001
From: Miles Malerba
Date: Fri, 5 May 2017 14:31:05 -0700
Subject: [PATCH 5/5] address comments
---
src/demo-app/datepicker/datepicker-demo.html | 6 +--
src/lib/datepicker/datepicker-input.ts | 40 +++++++++++---------
src/lib/datepicker/datepicker.md | 6 +--
3 files changed, 29 insertions(+), 23 deletions(-)
diff --git a/src/demo-app/datepicker/datepicker-demo.html b/src/demo-app/datepicker/datepicker-demo.html
index a9f62c5ea86d..1f6f5a882802 100644
--- a/src/demo-app/datepicker/datepicker-demo.html
+++ b/src/demo-app/datepicker/datepicker-demo.html
@@ -37,9 +37,9 @@
Result
[max]="maxDate"
[mdDatepickerFilter]="filterOdd ? dateFilter : null"
placeholder="Pick a date">
- Too early!
- Too late!
- Date unavailable!
+ Too early!
+ Too late!
+ Date unavailable! implements AfterContentInit, ControlValueAcces
private _datepickerSubscription: Subscription;
- /** The form control validator for this input. */
- private _validator: ValidatorFn = Validators.compose([
- (control: AbstractControl): ValidationErrors | null => {
- return (!this.min || !control.value ||
- this._dateAdapter.compareDate(this.min, control.value) < 0) ?
- null : {'mdDatepickerMin': {'min': this.min, 'actual': control.value}};
- },
- (control: AbstractControl): ValidationErrors | null => {
- return (!this.max || !control.value ||
- this._dateAdapter.compareDate(this.max, control.value) > 0) ?
- null : {'mdDatepickerMax': {'max': this.max, 'actual': control.value}};
- },
- (control: AbstractControl): ValidationErrors | null => {
- return !this._dateFilter || !control.value || this._dateFilter(control.value) ?
- null : {'mdDatepickerFilter': true};
- },
- ]);
+ /** The form control validator for the min date. */
+ private _minValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
+ return (!this.min || !control.value ||
+ this._dateAdapter.compareDate(this.min, control.value) < 0) ?
+ null : {'mdDatepickerMin': {'min': this.min, 'actual': control.value}};
+ }
+
+ /** The form control validator for the max date. */
+ private _maxValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
+ return (!this.max || !control.value ||
+ this._dateAdapter.compareDate(this.max, control.value) > 0) ?
+ null : {'mdDatepickerMax': {'max': this.max, 'actual': control.value}};
+ }
+
+ /** The form control validator for the date filter. */
+ private _filterValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
+ return !this._dateFilter || !control.value || this._dateFilter(control.value) ?
+ null : {'mdDatepickerFilter': true};
+ }
+
+ /** The combined form control validator for this input. */
+ private _validator: ValidatorFn =
+ Validators.compose([this._minValidator, this._maxValidator, this._filterValidator]);
constructor(
private _elementRef: ElementRef,
diff --git a/src/lib/datepicker/datepicker.md b/src/lib/datepicker/datepicker.md
index 7f977bb39bb8..ec235cfcc3dc 100644
--- a/src/lib/datepicker/datepicker.md
+++ b/src/lib/datepicker/datepicker.md
@@ -73,7 +73,7 @@ the datepicker, see section on
[choosing a date implementation](#choosing-a-date-implementation-and-date-format-settings)).
A result of `true` indicates that the date is valid and a result of `false` indicates that it is
not. Again this will also disable the dates on the calendar that are invalid. However, one important
-difference between using `mdDatepcikerFilter` vs using `min` or `max` is that filtering out all
+difference between using `mdDatepickerFilter` vs using `min` or `max` is that filtering out all
dates before or after a certain point, will not prevent the user from advancing the calendar past
that point.
@@ -90,12 +90,12 @@ maxDate = new Date(2020, 11, 31);
In this example the user can back past 2005, but all of the dates before then will be unselectable.
They will not be able to go further back in the calendar than 2000. If they manually type in a date
-that is before the min, after the max, or filtered out the input will have validation errors.
+that is before the min, after the max, or filtered out, the input will have validation errors.
Each validation property has a different error that can be checked:
* A value that violates the `min` property will have a `mdDatepickerMin` error.
* A value that violates the `max` property will have a `mdDatepickerMax` error.
- * A value that violates the `mdDatepickerFilter` property will have a `mdDatepickerFitler` error.
+ * A value that violates the `mdDatepickerFilter` property will have a `mdDatepickerFilter` error.
### Touch UI mode
The datepicker normally opens as a popup under the input. However this is not ideal for touch