无法将Observable绑定到Mat Chips自动完成组件的问题
问题:Observable触发但MatAutocomplete下拉列表不加载数据
我搭建了一个搜索引擎,需要通过Observable获取下拉自动补全建议。Observable已经触发(tap操作里能看到日志输出),但模板里的下拉列表始终加载不出数据。
原HTML代码
<mat-form-field> <mat-chip-grid #chipGrid aria-label="Value selection"> @for (fruit of fruits(); track $index) { <mat-chip-row (removed)="remove(fruit)"> //logic for fow </mat-chip-row> } </mat-chip-grid> <input placeholder="Start typing to see suggestions" #fruitInput formControlName="value" [matChipInputFor]="chipGrid" [matAutocomplete]="auto" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"/> </mat-form-field> <div *ngIf="getChipsValueDropdowns(select.value, i) | async as accounts"> <mat-autocomplete #auto="matAutocomplete"> <mat-option *ngFor="let account of accounts" [value]="account">{{account.name}}</mat-option> </mat-autocomplete> </div> <mat-autocomplete #auto="matAutocomplete"> <mat-option> Loading ...</mat-option> </mat-autocomplete>
原TS代码
getChipsValueDropdowns(key: string, index: number): Observable<Account[]> { if (key === 'account') { return this.registerAccountListener(key, index) } return of([]) } registerAccountListener(key: string, index: number) { const control: FormControl = this.formArray.controls[index].get('key') as FormControl if (control.value === key) { return this.accountService.searchAccounts('SHE').pipe( debounceTime(300), distinctUntilChanged(), map((response: { body: Account[] | null }) => { return response.body ?? [] }), tap((accounts) => { console.log('Emitting accounts:', accounts) }), ) } return of([]) }
问题分析与修正方案
1. 重复模板引用导致绑定错误
模板里存在两个#auto="matAutocomplete",输入框的[matAutocomplete]="auto"会优先匹配最后一个(即显示Loading的那个),导致承载数据的autocomplete从未被关联到输入框。
2. Observable未关联输入框实时值
registerAccountListener中固定传参'SHE'调用接口,没有响应输入框的实时输入内容,即使有数据也不是用户需要的结果。
3. 模板方法调用引发重复订阅
*ngIf="getChipsValueDropdowns(...) | async"会在每次变更检测时重新创建Observable,导致重复订阅,且无法和输入值变化联动。
修正后的代码
HTML部分
合并重复的autocomplete,统一处理加载状态和数据展示:
<mat-form-field> <mat-chip-grid #chipGrid aria-label="Value selection"> @for (fruit of fruits(); track $index) { <mat-chip-row (removed)="remove(fruit)"> <!-- 你的chip业务逻辑 --> </mat-chip-row> } </mat-chip-grid> <input placeholder="Start typing to see suggestions" #fruitInput formControlName="value" [matChipInputFor]="chipGrid" [matAutocomplete]="auto" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"/> </mat-form-field> <mat-autocomplete #auto="matAutocomplete"> <!-- 加载状态 --> <mat-option *ngIf="!(accounts$ | async)">Loading ...</mat-option> <!-- 数据列表 --> <mat-option *ngFor="let account of accounts$ | async" [value]="account"> {{account.name}} </mat-option> </mat-autocomplete>
TS部分
将Observable与输入框的valueChanges绑定,避免重复创建Observable:
import { of, Observable } from 'rxjs'; import { debounceTime, distinctUntilChanged, switchMap, map, tap } from 'rxjs/operators'; import { FormControl, FormArray } from '@angular/forms'; // 组件内定义用于自动补全的Observable accounts$: Observable<Account[]> = of([]); // 假设组件初始化时获取对应的formArray索引 private index: number; // 替换为实际的formArray项索引 ngOnInit(): void { // 获取输入框对应的FormControl const valueControl = this.formArray.controls[this.index].get('value') as FormControl; // 绑定输入值变化到接口请求 this.accounts$ = valueControl.valueChanges.pipe( debounceTime(300), // 防抖减少请求 distinctUntilChanged(), // 输入值不变时不重复请求 switchMap(inputValue => { // 使用输入框实时值调用搜索接口 return this.accountService.searchAccounts(inputValue).pipe( map(response => response.body ?? []), // 处理空响应 tap(accounts => console.log('Emitting accounts:', accounts)) ); }) ); }
额外检查点
- 确认
Account接口的name字段拼写正确,与接口返回字段一致 - 验证
formArray的索引index正确,确保绑定的FormControl是目标输入框的控件 - 确认
accountService.searchAccounts返回的Observable能正常发出数据(日志已显示可忽略此条)
内容的提问来源于stack exchange,提问作者JackOrlen
相关产品推荐
相关产品推荐

