mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(datepicker): input not picking up changes if datepicker is assigned after init (#12546)
This commit is contained in:
committed by
Joey Perrott
parent
73a3d4ade2
commit
329962821b
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user