Angular Material自动完成字段过滤失效及相关报错求助
解决Angular Material自动完成实时过滤及ExpressionChangedAfterItHasBeenCheckedError问题
问题根源
- 实时过滤失效:当前代码中过滤逻辑依赖
myControl.valueChanges流,但输入框未绑定该FormControl,而是使用ngModel,导致值变化事件无法触发过滤流程。 - ExpressionChangedAfterItHasBeenCheckedError错误:同时混用
ngModel和FormControl会触发Angular变更检测冲突,再加上组件实现了ControlValueAccessor并通过@Input传递FormControl,进一步加剧了初始化阶段的值变更时机问题。
解决方案
1. 统一使用FormControl管理输入状态
移除所有ngModel相关绑定,改用FormControl统一控制输入值,避免双向绑定与表单控件的冲突。
2. 修正ControlValueAccessor实现
让组件内部的FormControl与ControlValueAccessor接口正确联动,确保外部值传递和内部值变化都能同步。
3. 调整过滤逻辑初始化
确保过滤流在组件初始化时就能正确监听FormControl的值变化,包括初始值。
修改后的完整代码
组件HTML代码
<div class="grid-container"> <mat-form-field appearance="outline"> <mat-label>{{label}}</mat-label> <input matInput placeholder="{{placeholder}}" [formControl]="myControl" [matAutocomplete]="auto" required> <button mat-icon-button matSuffix (click)="clearInput()"> <mat-icon>close</mat-icon> </button> <mat-autocomplete #auto="matAutocomplete"> <mat-option *ngFor="let option of filteredOptions | async" [value]="option"> {{option}} </mat-option> </mat-autocomplete> <mat-error *ngIf="myControl.invalid && (myControl.dirty || myControl.touched)"> {{getErrorMessage()}} </mat-error> </mat-form-field> <button mat-icon-button (click)="createNew()"> <mat-icon>add</mat-icon> </button> </div>
组件TS代码
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; import { map, Observable, startWith } from "rxjs"; import { forwardRef, OnInit } from '@angular/core'; @Component({ selector: 'app-action-input-field', templateUrl: './action-input-field.component.html', styleUrls: ['./action-input-field.component.css'], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ActionInputFieldComponent), multi: true } ] }) export class ActionInputFieldComponent implements OnInit, ControlValueAccessor { @Input() label: string | undefined; @Input() placeholder: string | undefined; // 内部管理FormControl,避免外部传入导致的冲突 myControl: FormControl = new FormControl(); options: string[] = ['One', 'Two', 'Three']; filteredOptions: Observable<string[]> | undefined; constructor() { } ngOnInit() { this.filteredOptions = this.myControl.valueChanges.pipe( startWith(this.myControl.value || ''), map(value => this._filter(value || '')), ); // 监听FormControl值变化,同步到ControlValueAccessor this.myControl.valueChanges.subscribe(value => { this.onChange(value); this.onTouched(); }); } private _filter(value: string): string[] { const filterValue = value.toLowerCase(); return this.options.filter(option => option.toLowerCase().includes(filterValue)); } writeValue(value: any): void { this.myControl.setValue(value, { emitEvent: false }); } registerOnChange(fn: any): void { this.onChange = fn; } registerOnTouched(fn: any): void { this.onTouched = fn; } getErrorMessage() { return 'Toto pole je povinné'; } onChange = (value: any) => {}; onTouched = () => {}; clearInput() { this.myControl.setValue(''); } createNew() { alert('create new'); } }
内容的提问来源于stack exchange,提问作者Majo
相关产品推荐
相关产品推荐

