You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular基于ControlValueAccessor自定义表单控件错误消息显示方法

问题原因

自定义控件无法显示错误的核心原因是:组件内部textInputFormControl是独立创建的实例,和外层表单绑定的FormControl没有做校验状态、错误信息的同步,外层配置的校验规则产生的错误根本不会传到内部控件里,自然拿不到要展示的错误消息。

实现方案

最简便的实现方式是直接注入NgControl拿到外层绑定的表单控件实例,替代内部独立创建的FormControl,不需要手动做值和状态的中转,所有校验状态、错误信息、值变更都会自动同步。
注意:需要删掉组件装饰器里providers数组中NG_VALUE_ACCESSOR的配置,否则会触发循环依赖报错。

修改后的组件代码

text-input.component.ts

import { Component, Input, OnInit } from '@angular/core';
import {
  ControlValueAccessor,
  FormControl,
  NgControl
} from '@angular/forms';
import { getErrorMessage } from '../form-fields.helper';

@Component({
  selector: 'x-text-input',
  templateUrl: './text-input.component.html',
  styleUrls: ['./text-input.component.scss']
})
export class TextInputComponent implements ControlValueAccessor {
  @Input() label: string;
  @Input() readonly: boolean;
  @Input() required: boolean;

  textInputFormControl: FormControl;
  onTouched = () => {};
  onChange: (_: string) => {};

  constructor(@Optional() @Self() public ngControl: NgControl) {
    if (this.ngControl) {
      this.ngControl.valueAccessor = this;
    }
  }

  ngOnInit(): void {
    // 直接复用外层绑定的FormControl实例,所有状态自动同步
    this.textInputFormControl = this.ngControl.control as FormControl;
  }

  writeValue(value: any): void {
    this.textInputFormControl?.setValue(value, { emitEvent: false });
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState(disabled: boolean): void {
    disabled
      ? this.textInputFormControl?.disable({ emitEvent: false })
      : this.textInputFormControl?.enable({ emitEvent: false });
  }

  getErrorText(): string {
    return getErrorMessage(this.textInputFormControl.errors);
  }
}

text-input.component.html

<div style="font-size: 12px">
  <mat-form-field appearance="outline">
    <mat-label>{{ label }}</mat-label>
    <input
      matInput
      [required]="required"
      [readonly]="readonly"
      [formControl]="textInputFormControl"
      (blur)="onTouched()"
    />
    <!-- 错误提示:控件失焦且校验不通过时显示 -->
    <mat-error *ngIf="textInputFormControl.invalid && textInputFormControl.touched">
      {{ getErrorText() }}
    </mat-error>
  </mat-form-field>
</div>
注意事项
  • 原来模板里绑定的(input)="change()"可以删掉,因为直接复用了外层的FormControl实例,值变更会自动同步,不需要手动调用onChange传值。
  • 给输入框加了(blur)="onTouched()"事件,保证用户输入完成失焦后才触发错误提示,避免输入过程中就弹出错误。
  • 补上了之前缺失的@Input() required声明,否则模板里的required绑定不会生效。
  • 这种实现方式和普通Angular Material输入框的行为完全一致,不管外层用[formControl]还是formControlName绑定,所有内置校验、自定义校验产生的错误都能自动获取到,不需要额外给组件传错误参数。

内容的提问来源于stack exchange,提问作者Athul aravind k

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 05:57:37