Skip to content

Commit

Permalink
fix(autocomplete): panel not closing on IE when selecting an option w…
Browse files Browse the repository at this point in the history
…ith an empty string display value (#9506)

Fixes the autocomplete panel reopening on IE11 when the user selects an option whose display value is an empty string.

Fixes #9479.
  • Loading branch information
crisbeto authored and tinayuangao committed Feb 3, 2018
1 parent e30852a commit 95ffe37
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 11 deletions.
30 changes: 19 additions & 11 deletions src/lib/autocomplete/autocomplete-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
private _portal: TemplatePortal;
private _componentDestroyed = false;

/** Old value of the native input. Used to work around issues with the `input` event on IE. */
private _previousValue: string | number | null;

/** Strategy that is used to position the panel. */
private _positionStrategy: ConnectedPositionStrategy;

Expand Down Expand Up @@ -301,25 +304,30 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
}

_handleInput(event: KeyboardEvent): void {
// We need to ensure that the input is focused, because IE will fire the `input`
// event on focus/blur/load if the input has a placeholder. See:
// https://connect.microsoft.com/IE/feedback/details/885747/
if (this._canOpen() && document.activeElement === event.target) {
let target = event.target as HTMLInputElement;
let value: number | string | null = target.value;

// Based on `NumberValueAccessor` from forms.
if (target.type === 'number') {
value = value == '' ? null : parseFloat(value);
}
let target = event.target as HTMLInputElement;
let value: number | string | null = target.value;

// Based on `NumberValueAccessor` from forms.
if (target.type === 'number') {
value = value == '' ? null : parseFloat(value);
}

// If the input has a placeholder, IE will fire the `input` event on page load,
// focus and blur, in addition to when the user actually changed the value. To
// filter out all of the extra events, we save the value on focus and between
// `input` events, and we check whether it changed.
// See: https://connect.microsoft.com/IE/feedback/details/885747/
if (this._canOpen() && this._previousValue !== value &&
document.activeElement === event.target) {
this._previousValue = value;
this._onChange(value);
this.openPanel();
}
}

_handleFocus(): void {
if (this._canOpen()) {
this._previousValue = this._element.nativeElement.value;
this._attachOverlay();
this._floatLabel(true);
}
Expand Down
27 changes: 27 additions & 0 deletions src/lib/autocomplete/autocomplete.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ describe('MatAutocomplete', () => {

// Changing value from 'Alabama' to 'al' to re-populate the option list,
// ensuring that 'California' is created new.
dispatchFakeEvent(input, 'focusin');
typeInElement('al', input);
fixture.detectChanges();
tick();
Expand Down Expand Up @@ -818,6 +819,7 @@ describe('MatAutocomplete', () => {
expect(overlayContainerElement.textContent)
.toEqual('', `Expected panel to close after ENTER key.`);

dispatchFakeEvent(input, 'focusin');
typeInElement('Alabama', input);
fixture.detectChanges();
tick();
Expand All @@ -828,6 +830,31 @@ describe('MatAutocomplete', () => {
.toContain('Alabama', `Expected panel to display when typing in input.`);
}));

it('should not open the panel if the `input` event was dispatched with changing the value',
fakeAsync(() => {
const trigger = fixture.componentInstance.trigger;

dispatchFakeEvent(input, 'focusin');
typeInElement('A', input);
fixture.detectChanges();
tick();

expect(trigger.panelOpen).toBe(true, 'Expected panel to be open.');

trigger.closePanel();
fixture.detectChanges();

expect(trigger.panelOpen).toBe(false, 'Expected panel to be closed.');

// Dispatch the event without actually changing the value
// to simulate what happen in some cases on IE.
dispatchFakeEvent(input, 'input');
fixture.detectChanges();
tick();

expect(trigger.panelOpen).toBe(false, 'Expected panel to stay closed.');
}));

it('should scroll to active options below the fold', () => {
const trigger = fixture.componentInstance.trigger;
const scrollContainer =
Expand Down

0 comments on commit 95ffe37

Please sign in to comment.