From 1478c8d342714e154b639345d47f7adb8cad7e94 Mon Sep 17 00:00:00 2001 From: Karan Mistry Date: Fri, 27 Jun 2025 15:31:42 +0530 Subject: [PATCH] fix(material/autocomplete): add support for initial value in form control Currently, when we have some initial value in formcontrol those are not marked as selected, this fix will check the initial value and if its matched, will be marked as selected. Fixes #29422 --- .../autocomplete/autocomplete-trigger.ts | 13 +++++ .../autocomplete/autocomplete.spec.ts | 55 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/src/material/autocomplete/autocomplete-trigger.ts b/src/material/autocomplete/autocomplete-trigger.ts index 7ddf9fc79ef4..23a7672e670d 100644 --- a/src/material/autocomplete/autocomplete-trigger.ts +++ b/src/material/autocomplete/autocomplete-trigger.ts @@ -227,6 +227,9 @@ export class MatAutocompleteTrigger this._canOpenOnNextFocus = this.panelOpen || !this._hasFocus(); }; + /** Value of the autocomplete control. */ + private _value: any; + /** `View -> model callback called when value changes` */ _onChange: (value: any) => void = () => {}; @@ -273,6 +276,15 @@ export class MatAutocompleteTrigger ngAfterViewInit() { this._initialized.next(); this._initialized.complete(); + if (this._value) { + const selectedOption = this.autocomplete?.options?.find( + o => this._getDisplayValue(o.value) === this._getDisplayValue(this._value), + ); + if (selectedOption && !selectedOption.selected) { + selectedOption.select(false); + this._changeDetectorRef.detectChanges(); + } + } this._cleanupWindowBlur = this._renderer.listen('window', 'blur', this._windowBlurHandler); } @@ -455,6 +467,7 @@ export class MatAutocompleteTrigger // Implemented as part of ControlValueAccessor. writeValue(value: any): void { + this._value = value; Promise.resolve(null).then(() => this._assignOptionValue(value)); } diff --git a/src/material/autocomplete/autocomplete.spec.ts b/src/material/autocomplete/autocomplete.spec.ts index 5284c4ff922d..3baba5866068 100644 --- a/src/material/autocomplete/autocomplete.spec.ts +++ b/src/material/autocomplete/autocomplete.spec.ts @@ -1607,6 +1607,36 @@ describe('MatAutocomplete', () => { }); }); + describe('form control with initial value', () => { + let fixture: ComponentFixture; + let input: HTMLInputElement; + + beforeEach(waitForAsync(async () => { + fixture = createComponent(FormControlWithInitialValue); + fixture.detectChanges(); + + input = fixture.debugElement.query(By.css('input'))!.nativeElement; + + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + await new Promise(r => setTimeout(r)); + })); + + it('should mark the initial value as selected if its present', fakeAsync(() => { + const trigger = fixture.componentInstance.trigger; + trigger.openPanel(); + fixture.detectChanges(); + + const options = overlayContainerElement.querySelectorAll( + 'mat-option', + ) as NodeListOf; + + expect(input.value).toBe('Three'); + expect(options.length).toBe(3); + expect(options[2].classList).toContain('mdc-list-item--selected'); + })); + }); + describe('option groups', () => { let DOWN_ARROW_EVENT: KeyboardEvent; let UP_ARROW_EVENT: KeyboardEvent; @@ -4406,6 +4436,31 @@ class PlainAutocompleteInputWithFormControl { formControl = new FormControl(''); } +@Component({ + template: ` + + + + + @for (option of options; track option) { + + {{option}} + +} + + `, + imports: [MatAutocomplete, MatAutocompleteTrigger, MatOption, ReactiveFormsModule], +}) +class FormControlWithInitialValue { + optionCtrl = new FormControl('Three'); + filteredOptions: Observable; + options = ['One', 'Two', 'Three']; + + @ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger; + + constructor() {} +} + @Component({ template: `