Skip to content

Commit

Permalink
fix(material/autocomplete): clear selected option if input is cleared (
Browse files Browse the repository at this point in the history
…#27422)

Fixes that the selected state was lingering on the selected option even after the input value is cleared.

Fixes #26761.

(cherry picked from commit 7c8a796)
  • Loading branch information
crisbeto committed Jul 8, 2023
1 parent 8c6b2d3 commit ff2143b
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 12 deletions.
18 changes: 12 additions & 6 deletions src/material/autocomplete/autocomplete-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,10 @@ export abstract class _MatAutocompleteTriggerBase
this._pendingAutoselectedOption = null;
this._onChange(value);

if (!value) {
this._clearPreviousSelectedOption(null, false);
}

if (this._canOpen() && this._document.activeElement === event.target) {
this.openPanel();
}
Expand Down Expand Up @@ -628,12 +632,14 @@ export abstract class _MatAutocompleteTriggerBase
/**
* Clear any previous selected option and emit a selection change event for this option
*/
private _clearPreviousSelectedOption(skip: _MatOptionBase) {
this.autocomplete.options.forEach(option => {
if (option !== skip && option.selected) {
option.deselect();
}
});
private _clearPreviousSelectedOption(skip: _MatOptionBase | null, emitEvent?: boolean) {
if (this.autocomplete && this.autocomplete.options) {
this.autocomplete.options.forEach(option => {
if (option !== skip && option.selected) {
option.deselect(emitEvent);
}
});
}
}

private _attachOverlay(): void {
Expand Down
30 changes: 30 additions & 0 deletions src/material/autocomplete/autocomplete.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2474,6 +2474,36 @@ describe('MDC-based MatAutocomplete', () => {
.withContext(`Expected panel switch to the above position if the options no longer fit.`)
.toBe(Math.floor(panelBottom));
}));

it('should clear the selected option when the input value is cleared', fakeAsync(() => {
fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();
zone.simulateZoneExit();

const input = fixture.nativeElement.querySelector('input');
const option = overlayContainerElement.querySelector('mat-option') as HTMLElement;
const optionInstance = fixture.componentInstance.options.first;
const spy = jasmine.createSpy('selectionChange spy');

option.click();
fixture.detectChanges();
tick();

expect(input.value).toBe('Alabama');
expect(optionInstance.selected).toBe(true);

const subscription = optionInstance.onSelectionChange.subscribe(spy);

clearElement(input);
fixture.detectChanges();
tick();

expect(input.value).toBe('');
expect(optionInstance.selected).toBe(false);
expect(spy).not.toHaveBeenCalled();

subscription.unsubscribe();
}));
});

describe('panel closing', () => {
Expand Down
14 changes: 10 additions & 4 deletions src/material/core/option/option.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,20 +125,26 @@ export class _MatOptionBase<T = any> implements FocusableOption, AfterViewChecke
}

/** Selects the option. */
select(): void {
select(emitEvent = true): void {
if (!this._selected) {
this._selected = true;
this._changeDetectorRef.markForCheck();
this._emitSelectionChangeEvent();

if (emitEvent) {
this._emitSelectionChangeEvent();
}
}
}

/** Deselects the option. */
deselect(): void {
deselect(emitEvent = true): void {
if (this._selected) {
this._selected = false;
this._changeDetectorRef.markForCheck();
this._emitSelectionChangeEvent();

if (emitEvent) {
this._emitSelectionChangeEvent();
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions tools/public_api_guard/material/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ export class _MatOptionBase<T = any> implements FocusableOption, AfterViewChecke
get active(): boolean;
// (undocumented)
_changeDetectorRef: ChangeDetectorRef;
deselect(): void;
deselect(emitEvent?: boolean): void;
get disabled(): boolean;
set disabled(value: BooleanInput);
get disableRipple(): boolean;
Expand All @@ -293,7 +293,7 @@ export class _MatOptionBase<T = any> implements FocusableOption, AfterViewChecke
// (undocumented)
ngOnDestroy(): void;
readonly onSelectionChange: EventEmitter<MatOptionSelectionChange<T>>;
select(): void;
select(emitEvent?: boolean): void;
get selected(): boolean;
_selectViaInteraction(): void;
setActiveStyles(): void;
Expand Down

0 comments on commit ff2143b

Please sign in to comment.