fix(datepicker): input not picking up changes if datepicker is assigned after init (#12546)

This commit is contained in:
Kristiyan Kostadinov
2018-08-07 22:36:59 +02:00
committed by Joey Perrott
parent 73a3d4ade2
commit 329962821b
2 changed files with 85 additions and 26 deletions
+17 -24
View File
@@ -9,7 +9,6 @@
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {DOWN_ARROW} from '@angular/cdk/keycodes';
import {
AfterContentInit,
Directive,
ElementRef,
EventEmitter,
@@ -92,22 +91,28 @@ export class MatDatepickerInputEvent<D> {
},
exportAs: 'matDatepickerInput',
})
export class MatDatepickerInput<D> implements AfterContentInit, ControlValueAccessor, OnDestroy,
Validator {
export class MatDatepickerInput<D> implements ControlValueAccessor, OnDestroy, Validator {
/** The datepicker that this input is associated with. */
@Input()
set matDatepicker(value: MatDatepicker<D>) {
this.registerDatepicker(value);
if (!value) {
return;
}
this._datepicker = value;
this._datepicker._registerInput(this);
this._datepickerSubscription.unsubscribe();
this._datepickerSubscription = this._datepicker._selectedChanged.subscribe((selected: D) => {
this.value = selected;
this._cvaOnChange(selected);
this._onTouched();
this.dateInput.emit(new MatDatepickerInputEvent(this, this._elementRef.nativeElement));
this.dateChange.emit(new MatDatepickerInputEvent(this, this._elementRef.nativeElement));
});
}
_datepicker: MatDatepicker<D>;
private registerDatepicker(value: MatDatepicker<D>) {
if (value) {
this._datepicker = value;
this._datepicker._registerInput(this);
}
}
/** Function that can be used to filter out dates within the datepicker. */
@Input()
set matDatepickerFilter(value: (date: D | null) => boolean) {
@@ -252,18 +257,6 @@ export class MatDatepickerInput<D> implements AfterContentInit, ControlValueAcce
});
}
ngAfterContentInit() {
if (this._datepicker) {
this._datepickerSubscription = this._datepicker._selectedChanged.subscribe((selected: D) => {
this.value = selected;
this._cvaOnChange(selected);
this._onTouched();
this.dateInput.emit(new MatDatepickerInputEvent(this, this._elementRef.nativeElement));
this.dateChange.emit(new MatDatepickerInputEvent(this, this._elementRef.nativeElement));
});
}
}
ngOnDestroy() {
this._datepickerSubscription.unsubscribe();
this._localeSubscription.unsubscribe();
@@ -318,7 +311,7 @@ export class MatDatepickerInput<D> implements AfterContentInit, ControlValueAcce
}
_onKeydown(event: KeyboardEvent) {
if (event.altKey && event.keyCode === DOWN_ARROW) {
if (this._datepicker && event.altKey && event.keyCode === DOWN_ARROW) {
this._datepicker.open();
event.preventDefault();
}
+68 -2
View File
@@ -1,4 +1,4 @@
import {ENTER, ESCAPE, RIGHT_ARROW, UP_ARROW} from '@angular/cdk/keycodes';
import {ENTER, ESCAPE, RIGHT_ARROW, UP_ARROW, DOWN_ARROW} from '@angular/cdk/keycodes';
import {Overlay, OverlayContainer, ScrollDispatcher} from '@angular/cdk/overlay';
import {
createKeyboardEvent,
@@ -432,15 +432,66 @@ describe('MatDatepicker', () => {
expect(testComponent.datepicker.opened).toBe(false);
}));
it('should open the datpeicker using ALT + DOWN_ARROW', fakeAsync(() => {
expect(testComponent.datepicker.opened).toBe(false);
const event = createKeyboardEvent('keydown', DOWN_ARROW);
Object.defineProperty(event, 'altKey', {get: () => true});
dispatchEvent(fixture.nativeElement.querySelector('input'), event);
fixture.detectChanges();
flush();
expect(testComponent.datepicker.opened).toBe(true);
expect(event.defaultPrevented).toBe(true);
}));
});
describe('datepicker with too many inputs', () => {
it('should throw when multiple inputs registered', fakeAsync(() => {
let fixture = createComponent(MultiInputDatepicker, [MatNativeDateModule]);
const fixture = createComponent(MultiInputDatepicker, [MatNativeDateModule]);
expect(() => fixture.detectChanges()).toThrow();
}));
});
describe('datepicker that is assigned to input at a later point', () => {
it('should not throw on ALT + DOWN_ARROW for input without datepicker', fakeAsync(() => {
const fixture = createComponent(DelayedDatepicker, [MatNativeDateModule]);
fixture.detectChanges();
expect(() => {
const event = createKeyboardEvent('keydown', DOWN_ARROW);
Object.defineProperty(event, 'altKey', {get: () => true});
dispatchEvent(fixture.nativeElement.querySelector('input'), event);
fixture.detectChanges();
flush();
}).not.toThrow();
}));
it('should handle value changes when a datepicker is assigned after init', fakeAsync(() => {
const fixture = createComponent(DelayedDatepicker, [MatNativeDateModule]);
const testComponent: DelayedDatepicker = fixture.componentInstance;
const toSelect = new Date(2017, JAN, 1);
fixture.detectChanges();
expect(testComponent.datepickerInput.value).toBeNull();
expect(testComponent.datepicker._selected).toBeNull();
testComponent.assignedDatepicker = testComponent.datepicker;
fixture.detectChanges();
testComponent.assignedDatepicker.select(toSelect);
fixture.detectChanges();
flush();
fixture.detectChanges();
expect(testComponent.datepickerInput.value).toEqual(toSelect);
expect(testComponent.datepicker._selected).toEqual(toSelect);
}));
});
describe('datepicker with no inputs', () => {
let fixture: ComponentFixture<NoInputDatepicker>;
let testComponent: NoInputDatepicker;
@@ -1787,3 +1838,18 @@ class DatepickerWithCustomHeader {
`,
})
class CustomHeaderForDatepicker {}
@Component({
template: `
<input [matDatepicker]="assignedDatepicker" [value]="date">
<mat-datepicker #d [touchUi]="touch"></mat-datepicker>
`,
})
class DelayedDatepicker {
@ViewChild('d') datepicker: MatDatepicker<Date>;
@ViewChild(MatDatepickerInput) datepickerInput: MatDatepickerInput<Date>;
date: Date | null;
assignedDatepicker: MatDatepicker<Date>;
}