Skip to content

Commit cf868a5

Browse files
authored
fix(material/autocomplete): clear selected option if it is removed while typing (#28146)
Based on an internal bug report. Clears the selected option in the autocomplete if the user types in something else.
1 parent a6582b5 commit cf868a5

File tree

2 files changed

+75
-6
lines changed

2 files changed

+75
-6
lines changed

src/material/autocomplete/autocomplete-trigger.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,18 @@ export class MatAutocompleteTrigger
506506

507507
if (!value) {
508508
this._clearPreviousSelectedOption(null, false);
509+
} else if (this.panelOpen && !this.autocomplete.requireSelection) {
510+
// Note that we don't reset this when `requireSelection` is enabled,
511+
// because the option will be reset when the panel is closed.
512+
const selectedOption = this.autocomplete.options?.find(option => option.selected);
513+
514+
if (selectedOption) {
515+
const display = this.autocomplete.displayWith?.(selectedOption) ?? selectedOption.value;
516+
517+
if (value !== display) {
518+
selectedOption.deselect(false);
519+
}
520+
}
509521
}
510522

511523
if (this._canOpen() && this._document.activeElement === event.target) {
@@ -640,18 +652,16 @@ export class MatAutocompleteTrigger
640652
? this.autocomplete.displayWith(value)
641653
: value;
642654

655+
if (value == null) {
656+
this._clearPreviousSelectedOption(null, false);
657+
}
658+
643659
// Simply falling back to an empty string if the display value is falsy does not work properly.
644660
// The display value can also be the number zero and shouldn't fall back to an empty string.
645661
this._updateNativeInputValue(toDisplay != null ? toDisplay : '');
646662
}
647663

648664
private _updateNativeInputValue(value: string): void {
649-
// We want to clear the previous selection if our new value is falsy. e.g: reactive form field
650-
// being reset.
651-
if (!value) {
652-
this._clearPreviousSelectedOption(null, false);
653-
}
654-
655665
// If it's used within a `MatFormField`, we should set it through the property so it can go
656666
// through change detection.
657667
if (this._formField) {

src/material/autocomplete/autocomplete.spec.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,65 @@ describe('MDC-based MatAutocomplete', () => {
705705
}).not.toThrow();
706706
});
707707

708+
it('should clear the selected option if it no longer matches the input text while typing', fakeAsync(() => {
709+
const fixture = createComponent(SimpleAutocomplete);
710+
fixture.detectChanges();
711+
tick();
712+
713+
fixture.componentInstance.trigger.openPanel();
714+
fixture.detectChanges();
715+
zone.simulateZoneExit();
716+
717+
// Select an option and reopen the panel.
718+
(overlayContainerElement.querySelector('mat-option') as HTMLElement).click();
719+
fixture.detectChanges();
720+
tick();
721+
fixture.detectChanges();
722+
fixture.componentInstance.trigger.openPanel();
723+
fixture.detectChanges();
724+
zone.simulateZoneExit();
725+
726+
expect(fixture.componentInstance.options.first.selected).toBe(true);
727+
728+
const input = fixture.debugElement.query(By.css('input'))!.nativeElement;
729+
input.value = '';
730+
typeInElement(input, 'Ala');
731+
fixture.detectChanges();
732+
tick();
733+
734+
expect(fixture.componentInstance.options.first.selected).toBe(false);
735+
}));
736+
737+
it('should not clear the selected option if it no longer matches the input text while typing with requireSelection', fakeAsync(() => {
738+
const fixture = createComponent(SimpleAutocomplete);
739+
fixture.componentInstance.requireSelection = true;
740+
fixture.detectChanges();
741+
tick();
742+
743+
fixture.componentInstance.trigger.openPanel();
744+
fixture.detectChanges();
745+
zone.simulateZoneExit();
746+
747+
// Select an option and reopen the panel.
748+
(overlayContainerElement.querySelector('mat-option') as HTMLElement).click();
749+
fixture.detectChanges();
750+
tick();
751+
fixture.detectChanges();
752+
fixture.componentInstance.trigger.openPanel();
753+
fixture.detectChanges();
754+
zone.simulateZoneExit();
755+
756+
expect(fixture.componentInstance.options.first.selected).toBe(true);
757+
758+
const input = fixture.debugElement.query(By.css('input'))!.nativeElement;
759+
input.value = '';
760+
typeInElement(input, 'Ala');
761+
fixture.detectChanges();
762+
tick();
763+
764+
expect(fixture.componentInstance.options.first.selected).toBe(true);
765+
}));
766+
708767
describe('forms integration', () => {
709768
let fixture: ComponentFixture<SimpleAutocomplete>;
710769
let input: HTMLInputElement;

0 commit comments

Comments
 (0)