Skip to content

Commit 7c8a796

Browse files
authored
fix(material/autocomplete): clear selected option if input is cleared (#27422)
Fixes that the selected state was lingering on the selected option even after the input value is cleared. Fixes #26761.
1 parent 5f5c516 commit 7c8a796

File tree

4 files changed

+54
-12
lines changed

4 files changed

+54
-12
lines changed

src/material/autocomplete/autocomplete-trigger.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,10 @@ export abstract class _MatAutocompleteTriggerBase
465465
this._pendingAutoselectedOption = null;
466466
this._onChange(value);
467467

468+
if (!value) {
469+
this._clearPreviousSelectedOption(null, false);
470+
}
471+
468472
if (this._canOpen() && this._document.activeElement === event.target) {
469473
this.openPanel();
470474
}
@@ -628,12 +632,14 @@ export abstract class _MatAutocompleteTriggerBase
628632
/**
629633
* Clear any previous selected option and emit a selection change event for this option
630634
*/
631-
private _clearPreviousSelectedOption(skip: _MatOptionBase) {
632-
this.autocomplete.options.forEach(option => {
633-
if (option !== skip && option.selected) {
634-
option.deselect();
635-
}
636-
});
635+
private _clearPreviousSelectedOption(skip: _MatOptionBase | null, emitEvent?: boolean) {
636+
if (this.autocomplete && this.autocomplete.options) {
637+
this.autocomplete.options.forEach(option => {
638+
if (option !== skip && option.selected) {
639+
option.deselect(emitEvent);
640+
}
641+
});
642+
}
637643
}
638644

639645
private _attachOverlay(): void {

src/material/autocomplete/autocomplete.spec.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2474,6 +2474,36 @@ describe('MDC-based MatAutocomplete', () => {
24742474
.withContext(`Expected panel switch to the above position if the options no longer fit.`)
24752475
.toBe(Math.floor(panelBottom));
24762476
}));
2477+
2478+
it('should clear the selected option when the input value is cleared', fakeAsync(() => {
2479+
fixture.componentInstance.trigger.openPanel();
2480+
fixture.detectChanges();
2481+
zone.simulateZoneExit();
2482+
2483+
const input = fixture.nativeElement.querySelector('input');
2484+
const option = overlayContainerElement.querySelector('mat-option') as HTMLElement;
2485+
const optionInstance = fixture.componentInstance.options.first;
2486+
const spy = jasmine.createSpy('selectionChange spy');
2487+
2488+
option.click();
2489+
fixture.detectChanges();
2490+
tick();
2491+
2492+
expect(input.value).toBe('Alabama');
2493+
expect(optionInstance.selected).toBe(true);
2494+
2495+
const subscription = optionInstance.onSelectionChange.subscribe(spy);
2496+
2497+
clearElement(input);
2498+
fixture.detectChanges();
2499+
tick();
2500+
2501+
expect(input.value).toBe('');
2502+
expect(optionInstance.selected).toBe(false);
2503+
expect(spy).not.toHaveBeenCalled();
2504+
2505+
subscription.unsubscribe();
2506+
}));
24772507
});
24782508

24792509
describe('panel closing', () => {

src/material/core/option/option.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,20 +125,26 @@ export class _MatOptionBase<T = any> implements FocusableOption, AfterViewChecke
125125
}
126126

127127
/** Selects the option. */
128-
select(): void {
128+
select(emitEvent = true): void {
129129
if (!this._selected) {
130130
this._selected = true;
131131
this._changeDetectorRef.markForCheck();
132-
this._emitSelectionChangeEvent();
132+
133+
if (emitEvent) {
134+
this._emitSelectionChangeEvent();
135+
}
133136
}
134137
}
135138

136139
/** Deselects the option. */
137-
deselect(): void {
140+
deselect(emitEvent = true): void {
138141
if (this._selected) {
139142
this._selected = false;
140143
this._changeDetectorRef.markForCheck();
141-
this._emitSelectionChangeEvent();
144+
145+
if (emitEvent) {
146+
this._emitSelectionChangeEvent();
147+
}
142148
}
143149
}
144150

tools/public_api_guard/material/core.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ export class _MatOptionBase<T = any> implements FocusableOption, AfterViewChecke
274274
get active(): boolean;
275275
// (undocumented)
276276
_changeDetectorRef: ChangeDetectorRef;
277-
deselect(): void;
277+
deselect(emitEvent?: boolean): void;
278278
get disabled(): boolean;
279279
set disabled(value: BooleanInput);
280280
get disableRipple(): boolean;
@@ -293,7 +293,7 @@ export class _MatOptionBase<T = any> implements FocusableOption, AfterViewChecke
293293
// (undocumented)
294294
ngOnDestroy(): void;
295295
readonly onSelectionChange: EventEmitter<MatOptionSelectionChange<T>>;
296-
select(): void;
296+
select(emitEvent?: boolean): void;
297297
get selected(): boolean;
298298
_selectViaInteraction(): void;
299299
setActiveStyles(): void;

0 commit comments

Comments
 (0)