Skip to content

Commit 7fadec9

Browse files
committed
tweak handling of invalid dates.
1 parent 9676022 commit 7fadec9

File tree

5 files changed

+55
-39
lines changed

5 files changed

+55
-39
lines changed

src/lib/core/datetime/date-adapter.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,26 @@ export abstract class DateAdapter<D> {
157157
abstract getISODateString(date: D): string;
158158

159159
/**
160-
* Checks whether the given value is a valid date object.
161-
* @param value The value to check.
162-
* @returns Whether the value is a valid date object.
160+
* Checks whether the given object is considered a date instance by this DateAdapter.
161+
* @param obj The object to check
162+
* @returns Whether the object is a date instance.
163163
*/
164-
abstract isValidDate(value: any): boolean;
164+
abstract isDateInstance(obj: any): boolean;
165+
166+
/**
167+
* Checks whether the given date is valid.
168+
* @param date The date to check.
169+
* @returns Whether the date is valid.
170+
*/
171+
abstract isValid(date: D): boolean;
172+
173+
/**
174+
* @param {obj} The object to check.
175+
* @returns The given object if it is both a date instance and valid, otherwise null.
176+
*/
177+
getValidDateOrNull(obj: any): D | null {
178+
return (this.isDateInstance(obj) && this.isValid(obj)) ? obj : null;
179+
}
165180

166181
/**
167182
* Sets the locale used for all dates.

src/lib/core/datetime/native-date-adapter.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,15 +306,15 @@ describe('NativeDateAdapter', () => {
306306
});
307307

308308
it('should count a Date as a valid date object', () => {
309-
expect(adapter.isValidDate(new Date())).toBe(true);
309+
expect(adapter.isValid(new Date())).toBe(true);
310310
});
311311

312312
it('should not count a string as a valid date object', () => {
313-
expect(adapter.isValidDate('1/1/2017')).toBe(false);
313+
expect(adapter.isValid('1/1/2017')).toBe(false);
314314
});
315315

316316
it('should not count InvalidDate as a valid date object', () => {
317-
expect(adapter.isValidDate(new Date(NaN))).toBe(false);
317+
expect(adapter.isValid(new Date(NaN))).toBe(false);
318318
});
319319
});
320320

src/lib/core/datetime/native-date-adapter.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ export class NativeDateAdapter extends DateAdapter<Date> {
164164
}
165165

166166
format(date: Date, displayFormat: Object): string {
167-
if (!this.isValidDate(date)) {
167+
if (!this.isValid(date)) {
168168
return 'INVALID DATE';
169169
}
170170
if (SUPPORTS_INTL_API) {
@@ -212,14 +212,12 @@ export class NativeDateAdapter extends DateAdapter<Date> {
212212
].join('-');
213213
}
214214

215-
isValidDate(value: any) {
216-
if (value == null) {
217-
return true;
218-
}
219-
if (value instanceof Date) {
220-
return !isNaN(value.getTime());
221-
}
222-
return false;
215+
isDateInstance(obj: any) {
216+
return obj instanceof Date;
217+
}
218+
219+
isValid(date: Date) {
220+
return !isNaN(date.getTime());
223221
}
224222

225223
/** Creates a date but allows the month and date to overflow. */

src/lib/datepicker/datepicker-input.ts

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,16 @@ export class MdDatepickerInput<D> implements AfterContentInit, ControlValueAcces
112112
/** The value of the input. */
113113
@Input()
114114
get value(): D | null {
115-
return this._dateAdapter.parse(this._elementRef.nativeElement.value,
116-
this._dateFormats.parse.dateInput);
115+
return this._dateAdapter.getValidDateOrNull(
116+
this._dateAdapter.parse(
117+
this._elementRef.nativeElement.value, this._dateFormats.parse.dateInput));
117118
}
118119
set value(value: D | null) {
120+
if (value != null && !this._dateAdapter.isDateInstance(value)) {
121+
throw Error('Datepicker: value not recognized as a date object by DateAdapter.');
122+
}
123+
value = this._dateAdapter.getValidDateOrNull(value);
124+
119125
let oldDate = this.value;
120126
this._renderer.setProperty(this._elementRef.nativeElement, 'value',
121127
value ? this._dateAdapter.format(value, this._dateFormats.display.dateInput) : '');
@@ -126,21 +132,21 @@ export class MdDatepickerInput<D> implements AfterContentInit, ControlValueAcces
126132

127133
/** The minimum valid date. */
128134
@Input()
129-
get min(): D { return this._min; }
130-
set min(value: D) {
131-
this._min = value;
135+
get min(): D | null { return this._min; }
136+
set min(value: D | null) {
137+
this._min = this._dateAdapter.getValidDateOrNull(value);
132138
this._validatorOnChange();
133139
}
134-
private _min: D;
140+
private _min: D | null;
135141

136142
/** The maximum valid date. */
137143
@Input()
138-
get max(): D { return this._max; }
139-
set max(value: D) {
140-
this._max = value;
144+
get max(): D | null { return this._max; }
145+
set max(value: D | null) {
146+
this._max = this._dateAdapter.getValidDateOrNull(value);
141147
this._validatorOnChange();
142148
}
143-
private _max: D;
149+
private _max: D | null;
144150

145151
/** Whether the datepicker-input is disabled. */
146152
@Input()
@@ -169,20 +175,20 @@ export class MdDatepickerInput<D> implements AfterContentInit, ControlValueAcces
169175

170176
/** The form control validator for whether the input parses. */
171177
private _parseValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
172-
return (!control.value || this._dateAdapter.isValidDate(control.value)) ?
178+
return (!control.value || this._dateAdapter.isValid(control.value)) ?
173179
null : {'mdDatepickerParse': true};
174180
}
175181

176182
/** The form control validator for the min date. */
177183
private _minValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
178-
return (!this.min || !this._dateAdapter.isValidDate(this.min) || !control.value ||
184+
return (!this.min || !control.value ||
179185
this._dateAdapter.compareDate(this.min, control.value) <= 0) ?
180186
null : {'mdDatepickerMin': {'min': this.min, 'actual': control.value}};
181187
}
182188

183189
/** The form control validator for the max date. */
184190
private _maxValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
185-
return (!this.max || !this._dateAdapter.isValidDate(this.max) || !control.value ||
191+
return (!this.max || !control.value ||
186192
this._dateAdapter.compareDate(this.max, control.value) >= 0) ?
187193
null : {'mdDatepickerMax': {'max': this.max, 'actual': control.value}};
188194
}

src/lib/datepicker/datepicker.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -124,15 +124,13 @@ export class MdDatepickerContent<D> implements AfterContentInit {
124124
export class MdDatepicker<D> implements OnDestroy {
125125
/** The date to open the calendar to initially. */
126126
@Input()
127-
get startAt(): D {
127+
get startAt(): D | null {
128128
// If an explicit startAt is set we start there, otherwise we start at whatever the currently
129129
// selected value is.
130-
return this._startAt ||
131-
(this._datepickerInput && this._dateAdapter.isValidDate(this._datepickerInput.value) ?
132-
this._datepickerInput.value : null);
130+
return this._startAt || (this._datepickerInput ? this._datepickerInput.value : null);
133131
}
134-
set startAt(date: D) { this._startAt = date; }
135-
private _startAt: D;
132+
set startAt(date: D | null) { this._startAt = this._dateAdapter.getValidDateOrNull(date); }
133+
private _startAt: D | null;
136134

137135
/** The view that the calendar should start in. */
138136
@Input() startView: 'month' | 'year' = 'month';
@@ -166,12 +164,12 @@ export class MdDatepicker<D> implements OnDestroy {
166164
_selected: D | null = null;
167165

168166
/** The minimum selectable date. */
169-
get _minDate(): D {
167+
get _minDate(): D | null {
170168
return this._datepickerInput && this._datepickerInput.min;
171169
}
172170

173171
/** The maximum selectable date. */
174-
get _maxDate(): D {
172+
get _maxDate(): D | null {
175173
return this._datepickerInput && this._datepickerInput.max;
176174
}
177175

@@ -239,8 +237,7 @@ export class MdDatepicker<D> implements OnDestroy {
239237
}
240238
this._datepickerInput = input;
241239
this._inputSubscription =
242-
this._datepickerInput._valueChange.subscribe((value: D) =>
243-
this._selected = this._dateAdapter.isValidDate(value) ? value : null);
240+
this._datepickerInput._valueChange.subscribe((value: D | null) => this._selected = value);
244241
}
245242

246243
/** Open the calendar. */

0 commit comments

Comments
 (0)