Skip to content

Commit d10a6c4

Browse files
crisbetojosephperrott
authored andcommitted
fix(datepicker): input not picking up changes if datepicker is assigned after init (#12546)
1 parent 35bdd00 commit d10a6c4

File tree

2 files changed

+85
-26
lines changed

2 files changed

+85
-26
lines changed

src/lib/datepicker/datepicker-input.ts

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import {coerceBooleanProperty} from '@angular/cdk/coercion';
1010
import {DOWN_ARROW} from '@angular/cdk/keycodes';
1111
import {
12-
AfterContentInit,
1312
Directive,
1413
ElementRef,
1514
EventEmitter,
@@ -92,21 +91,27 @@ export class MatDatepickerInputEvent<D> {
9291
},
9392
exportAs: 'matDatepickerInput',
9493
})
95-
export class MatDatepickerInput<D> implements AfterContentInit, ControlValueAccessor, OnDestroy,
96-
Validator {
94+
export class MatDatepickerInput<D> implements ControlValueAccessor, OnDestroy, Validator {
9795
/** The datepicker that this input is associated with. */
9896
@Input()
9997
set matDatepicker(value: MatDatepicker<D>) {
100-
this.registerDatepicker(value);
101-
}
102-
_datepicker: MatDatepicker<D>;
103-
104-
private registerDatepicker(value: MatDatepicker<D>) {
105-
if (value) {
106-
this._datepicker = value;
107-
this._datepicker._registerInput(this);
98+
if (!value) {
99+
return;
108100
}
101+
102+
this._datepicker = value;
103+
this._datepicker._registerInput(this);
104+
this._datepickerSubscription.unsubscribe();
105+
106+
this._datepickerSubscription = this._datepicker._selectedChanged.subscribe((selected: D) => {
107+
this.value = selected;
108+
this._cvaOnChange(selected);
109+
this._onTouched();
110+
this.dateInput.emit(new MatDatepickerInputEvent(this, this._elementRef.nativeElement));
111+
this.dateChange.emit(new MatDatepickerInputEvent(this, this._elementRef.nativeElement));
112+
});
109113
}
114+
_datepicker: MatDatepicker<D>;
110115

111116
/** Function that can be used to filter out dates within the datepicker. */
112117
@Input()
@@ -252,18 +257,6 @@ export class MatDatepickerInput<D> implements AfterContentInit, ControlValueAcce
252257
});
253258
}
254259

255-
ngAfterContentInit() {
256-
if (this._datepicker) {
257-
this._datepickerSubscription = this._datepicker._selectedChanged.subscribe((selected: D) => {
258-
this.value = selected;
259-
this._cvaOnChange(selected);
260-
this._onTouched();
261-
this.dateInput.emit(new MatDatepickerInputEvent(this, this._elementRef.nativeElement));
262-
this.dateChange.emit(new MatDatepickerInputEvent(this, this._elementRef.nativeElement));
263-
});
264-
}
265-
}
266-
267260
ngOnDestroy() {
268261
this._datepickerSubscription.unsubscribe();
269262
this._localeSubscription.unsubscribe();
@@ -318,7 +311,7 @@ export class MatDatepickerInput<D> implements AfterContentInit, ControlValueAcce
318311
}
319312

320313
_onKeydown(event: KeyboardEvent) {
321-
if (event.altKey && event.keyCode === DOWN_ARROW) {
314+
if (this._datepicker && event.altKey && event.keyCode === DOWN_ARROW) {
322315
this._datepicker.open();
323316
event.preventDefault();
324317
}

src/lib/datepicker/datepicker.spec.ts

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {ENTER, ESCAPE, RIGHT_ARROW, UP_ARROW} from '@angular/cdk/keycodes';
1+
import {ENTER, ESCAPE, RIGHT_ARROW, UP_ARROW, DOWN_ARROW} from '@angular/cdk/keycodes';
22
import {Overlay, OverlayContainer, ScrollDispatcher} from '@angular/cdk/overlay';
33
import {
44
createKeyboardEvent,
@@ -432,15 +432,66 @@ describe('MatDatepicker', () => {
432432
expect(testComponent.datepicker.opened).toBe(false);
433433
}));
434434

435+
it('should open the datpeicker using ALT + DOWN_ARROW', fakeAsync(() => {
436+
expect(testComponent.datepicker.opened).toBe(false);
437+
438+
const event = createKeyboardEvent('keydown', DOWN_ARROW);
439+
Object.defineProperty(event, 'altKey', {get: () => true});
440+
441+
dispatchEvent(fixture.nativeElement.querySelector('input'), event);
442+
fixture.detectChanges();
443+
flush();
444+
445+
expect(testComponent.datepicker.opened).toBe(true);
446+
expect(event.defaultPrevented).toBe(true);
447+
}));
448+
435449
});
436450

437451
describe('datepicker with too many inputs', () => {
438452
it('should throw when multiple inputs registered', fakeAsync(() => {
439-
let fixture = createComponent(MultiInputDatepicker, [MatNativeDateModule]);
453+
const fixture = createComponent(MultiInputDatepicker, [MatNativeDateModule]);
440454
expect(() => fixture.detectChanges()).toThrow();
441455
}));
442456
});
443457

458+
describe('datepicker that is assigned to input at a later point', () => {
459+
it('should not throw on ALT + DOWN_ARROW for input without datepicker', fakeAsync(() => {
460+
const fixture = createComponent(DelayedDatepicker, [MatNativeDateModule]);
461+
fixture.detectChanges();
462+
463+
expect(() => {
464+
const event = createKeyboardEvent('keydown', DOWN_ARROW);
465+
Object.defineProperty(event, 'altKey', {get: () => true});
466+
dispatchEvent(fixture.nativeElement.querySelector('input'), event);
467+
fixture.detectChanges();
468+
flush();
469+
}).not.toThrow();
470+
}));
471+
472+
it('should handle value changes when a datepicker is assigned after init', fakeAsync(() => {
473+
const fixture = createComponent(DelayedDatepicker, [MatNativeDateModule]);
474+
const testComponent: DelayedDatepicker = fixture.componentInstance;
475+
const toSelect = new Date(2017, JAN, 1);
476+
477+
fixture.detectChanges();
478+
479+
expect(testComponent.datepickerInput.value).toBeNull();
480+
expect(testComponent.datepicker._selected).toBeNull();
481+
482+
testComponent.assignedDatepicker = testComponent.datepicker;
483+
fixture.detectChanges();
484+
485+
testComponent.assignedDatepicker.select(toSelect);
486+
fixture.detectChanges();
487+
flush();
488+
fixture.detectChanges();
489+
490+
expect(testComponent.datepickerInput.value).toEqual(toSelect);
491+
expect(testComponent.datepicker._selected).toEqual(toSelect);
492+
}));
493+
});
494+
444495
describe('datepicker with no inputs', () => {
445496
let fixture: ComponentFixture<NoInputDatepicker>;
446497
let testComponent: NoInputDatepicker;
@@ -1787,3 +1838,18 @@ class DatepickerWithCustomHeader {
17871838
`,
17881839
})
17891840
class CustomHeaderForDatepicker {}
1841+
1842+
1843+
1844+
@Component({
1845+
template: `
1846+
<input [matDatepicker]="assignedDatepicker" [value]="date">
1847+
<mat-datepicker #d [touchUi]="touch"></mat-datepicker>
1848+
`,
1849+
})
1850+
class DelayedDatepicker {
1851+
@ViewChild('d') datepicker: MatDatepicker<Date>;
1852+
@ViewChild(MatDatepickerInput) datepickerInput: MatDatepickerInput<Date>;
1853+
date: Date | null;
1854+
assignedDatepicker: MatDatepicker<Date>;
1855+
}

0 commit comments

Comments
 (0)