Skip to content

Commit dff1c3f

Browse files
committed
Refactored date picker
1 parent c40a0fd commit dff1c3f

File tree

2 files changed

+66
-48
lines changed

2 files changed

+66
-48
lines changed

src/material/datepicker/datepicker-input.ts

Lines changed: 63 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,13 @@ import {
2929
ValidatorFn,
3030
Validators,
3131
} from '@angular/forms';
32-
import {DateAdapter, MAT_DATE_FORMATS, MatDateFormats, ThemePalette} from '@angular/material/core';
32+
import {
33+
DateAdapter,
34+
MAT_DATE_FORMATS,
35+
MatSingleDateSelectionModel,
36+
MatDateFormats,
37+
ThemePalette,
38+
} from '@angular/material/core';
3339
import {MatFormField} from '@angular/material/form-field';
3440
import {MAT_INPUT_VALUE_ACCESSOR} from '@angular/material/input';
3541
import {Subscription} from 'rxjs';
@@ -123,38 +129,58 @@ export class MatDatepickerInput<D> implements ControlValueAccessor, OnDestroy, V
123129

124130
/** The value of the input. */
125131
@Input()
126-
get value(): D | null { return this._value; }
132+
get value(): D | null { return this._value ? this._value.asDate() : null; }
127133
set value(value: D | null) {
128-
value = this._dateAdapter.deserialize(value);
129-
this._lastValueValid = !value || this._dateAdapter.isValid(value);
130-
value = this._getValidDateOrNull(value);
131-
const oldDate = this.value;
132-
this._value = value;
133-
this._formatValue(value);
134-
135-
if (!this._dateAdapter.sameDate(oldDate, value)) {
136-
this._valueChange.emit(value);
134+
value = this._dateAdapter.deserialize(value);
135+
const newDate = new MatSingleDateSelectionModel(this._dateAdapter, value);
136+
let isDifferent = false;
137+
if (!newDate.isSame(this._value)) {
138+
isDifferent = true;
139+
}
140+
if (this._value) {
141+
this._value.add(value);
142+
} else {
143+
this._value = newDate;
144+
}
145+
this._lastValueValid = !this._value || this._value.isValid();
146+
147+
if (this._value) {
148+
this._formatValue(this._value.asDate());
149+
}
150+
151+
if (isDifferent) {
152+
this._valueChange.emit(this.value);
137153
}
138154
}
139-
private _value: D | null;
155+
private _value: MatSingleDateSelectionModel<D>;
140156

141157
/** The minimum valid date. */
142158
@Input()
143-
get min(): D | null { return this._min; }
159+
get min(): D | null { return this._min ? this._min.asDate() : null; }
144160
set min(value: D | null) {
145-
this._min = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
161+
value = this._dateAdapter.deserialize(value);
162+
if (this._min) {
163+
this._min.add(value);
164+
} else {
165+
this._min = new MatSingleDateSelectionModel(this._dateAdapter, value);
166+
}
146167
this._validatorOnChange();
147168
}
148-
private _min: D | null;
169+
private _min: MatSingleDateSelectionModel<D>;
149170

150171
/** The maximum valid date. */
151172
@Input()
152-
get max(): D | null { return this._max; }
173+
get max(): D | null { return this._max ? this._max.asDate() : null; }
153174
set max(value: D | null) {
154-
this._max = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
175+
value = this._dateAdapter.deserialize(value);
176+
if (this._max) {
177+
this._max.add(value);
178+
} else {
179+
this._max = new MatSingleDateSelectionModel(this._dateAdapter, value);
180+
}
155181
this._validatorOnChange();
156182
}
157-
private _max: D | null;
183+
private _max: MatSingleDateSelectionModel<D>;
158184

159185
/** Whether the datepicker-input is disabled. */
160186
@Input()
@@ -210,25 +236,25 @@ export class MatDatepickerInput<D> implements ControlValueAccessor, OnDestroy, V
210236

211237
/** The form control validator for the min date. */
212238
private _minValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
213-
const controlValue = this._getValidDateOrNull(this._dateAdapter.deserialize(control.value));
214-
return (!this.min || !controlValue ||
215-
this._dateAdapter.compareDate(this.min, controlValue) <= 0) ?
216-
null : {'matDatepickerMin': {'min': this.min, 'actual': controlValue}};
239+
const controlValueSelection = new MatSingleDateSelectionModel(this._dateAdapter, control.value);
240+
return (!this.min || !controlValueSelection.isValid() ||
241+
this._min.compareDate(controlValueSelection) <= 0) ?
242+
null : {'matDatepickerMin': {'min': this.min, 'actual': controlValueSelection.asDate()}};
217243
}
218244

219245
/** The form control validator for the max date. */
220246
private _maxValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
221-
const controlValue = this._getValidDateOrNull(this._dateAdapter.deserialize(control.value));
222-
return (!this.max || !controlValue ||
223-
this._dateAdapter.compareDate(this.max, controlValue) >= 0) ?
224-
null : {'matDatepickerMax': {'max': this.max, 'actual': controlValue}};
247+
const controlValueSelection = new MatSingleDateSelectionModel(this._dateAdapter, control.value);
248+
return (!this.max || !controlValueSelection.isValid() ||
249+
this._max.compareDate(controlValueSelection) >= 0) ?
250+
null : {'matDatepickerMax': {'max': this.max, 'actual': controlValueSelection.asDate}};
225251
}
226252

227253
/** The form control validator for the date filter. */
228254
private _filterValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
229-
const controlValue = this._getValidDateOrNull(this._dateAdapter.deserialize(control.value));
230-
return !this._dateFilter || !controlValue || this._dateFilter(controlValue) ?
231-
null : {'matDatepickerFilter': true};
255+
const controlValueSelection = new MatSingleDateSelectionModel(this._dateAdapter, control.value);
256+
return !this._dateFilter || !controlValueSelection.isValid() ||
257+
this._dateFilter(controlValueSelection.asDate()) ? null : {'matDatepickerFilter': true};
232258
}
233259

234260
/** The combined form control validator for this input. */
@@ -320,14 +346,14 @@ export class MatDatepickerInput<D> implements ControlValueAccessor, OnDestroy, V
320346
}
321347

322348
_onInput(value: string) {
323-
let date = this._dateAdapter.parse(value, this._dateFormats.parse.dateInput);
324-
this._lastValueValid = !date || this._dateAdapter.isValid(date);
325-
date = this._getValidDateOrNull(date);
326-
327-
if (!this._dateAdapter.sameDate(date, this._value)) {
328-
this._value = date;
329-
this._cvaOnChange(date);
330-
this._valueChange.emit(date);
349+
const dateSelection = new MatSingleDateSelectionModel(this._dateAdapter,
350+
this._dateAdapter.parse(value, this._dateFormats.parse.dateInput));
351+
this._lastValueValid = dateSelection.isValid();
352+
353+
if (!dateSelection.isSame(this._value)) {
354+
this._value = dateSelection;
355+
this._cvaOnChange(dateSelection.asDate());
356+
this._valueChange.emit(dateSelection.asDate());
331357
this.dateInput.emit(new MatDatepickerInputEvent(this, this._elementRef.nativeElement));
332358
} else {
333359
this._validatorOnChange();
@@ -358,12 +384,4 @@ export class MatDatepickerInput<D> implements ControlValueAccessor, OnDestroy, V
358384
this._elementRef.nativeElement.value =
359385
value ? this._dateAdapter.format(value, this._dateFormats.display.dateInput) : '';
360386
}
361-
362-
/**
363-
* @param obj The object to check.
364-
* @returns The given object if it is both a date instance and valid, otherwise null.
365-
*/
366-
private _getValidDateOrNull(obj: any): D | null {
367-
return (this._dateAdapter.isDateInstance(obj) && this._dateAdapter.isValid(obj)) ? obj : null;
368-
}
369387
}

src/material/datepicker/datepicker.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,9 +1191,9 @@ describe('MatDatepicker', () => {
11911191
.not.toContain('ng-invalid');
11921192
}));
11931193

1194-
it('should update validity when switching between null and invalid', fakeAsync(() => {
1194+
it('should update validity when switching between valid and invalid', fakeAsync(() => {
11951195
const inputEl = fixture.debugElement.query(By.css('input'))!.nativeElement;
1196-
inputEl.value = '';
1196+
inputEl.value = new Date(2010, JAN, 2).toDateString();
11971197
dispatchFakeEvent(inputEl, 'input');
11981198

11991199
fixture.detectChanges();
@@ -1211,7 +1211,7 @@ describe('MatDatepicker', () => {
12111211

12121212
expect(testComponent.model.valid).toBe(false);
12131213

1214-
inputEl.value = '';
1214+
inputEl.value = new Date(2010, JAN, 2).toDateString();
12151215
dispatchFakeEvent(inputEl, 'input');
12161216

12171217
fixture.detectChanges();

0 commit comments

Comments
 (0)