Angular Material 的 matInput 指令完全指南:从原生输入框到 的无缝集成
【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/components
matInput是 Angular Material(当前仓库src/material/input)提供的一个指令,它让原生<input>、<textarea>甚至原生<select>元素能够直接与<mat-form-field>协同工作,从而获得占位符、标签浮动、错误提示、前后缀图标、主题化等完整表单字段能力。本文将基于 input.md 文档,并结合仓库源码(src/material/input/input.ts、src/material/core/error/error-options.ts)深入讲解其支持的类型、表单集成方式、错误状态控制、可访问性细节与常见问题排查,帮助你写出真正可落地、无障碍友好的 Material 表单。
matInput 是什么:让原生元素接入 Material 表单体系
matInput本身是一个指令(Directive)而非组件,它的核心价值在于"桥接":原生输入元素保持其固有的浏览器行为与无障碍语义,而<mat-form-field>则负责视觉呈现(标签、边框、浮动占位符)与交互状态管理(焦点、错误、禁用)。
从 input.ts 的指令定义可以看到它的完整选择器:
@Directive({ selector: `input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]`, exportAs: 'matInput', host: { 'class': 'mat-mdc-input-element', '[class.mat-mdc-form-field-input-control]': '_isInFormField', '[class.mdc-text-field__input]': '_isInFormField', // ... }, providers: [{provide: MatFormFieldControl, useExisting: MatInput}], }) export class MatInput implements MatFormFieldControl<any>, OnChanges, OnDestroy, AfterViewInit, DoCheck {}需要注意几点实现细节:
- 指令同时支持
matInput与matNativeControl两种属性标记,后者同样可作用于原生<select>; - 它通过
providers将自己注册为MatFormFieldControl,这是<mat-form-field>识别"哪个元素是我的控制项"的机制; - 宿主绑定同步了
id、disabled、required、name、readonly、aria-invalid、aria-required、aria-disabled等原生属性,确保 Angular 输入绑定与真实 DOM 属性保持一致(源码注释明确说明:原生属性被 Angular 输入覆盖后必须同步回原生元素,否则属性绑定不生效); - 焦点与输入事件被监听(
(focus)、(blur)、(input)),用于驱动表单字段的浮动标签与错误状态刷新。
最小的使用示例
仓库中 input-overview-example.ts 与对应的 input-overview-example.html 给出了最基础的用法:
<form class="example-form"> <mat-form-field class="example-full-width"> <mat-label>Favorite food</mat-label> <input matInput placeholder="Ex. Pizza" value="Sushi"> </mat-form-field> <mat-form-field class="example-full-width"> <mat-label>Leave a comment</mat-label> <textarea matInput placeholder="Ex. It makes me feel..."></textarea> </mat-form-field> </form>对应的组件需要导入FormsModule、MatFormFieldModule与MatInputModule:
import {Component} from '@angular/core'; import {MatInputModule} from '@angular/material/input'; import {MatFormFieldModule} from '@angular/material/form-field'; import {FormsModule} from '@angular/forms'; @Component({ selector: 'input-overview-example', templateUrl: 'input-overview-example.html', imports: [FormsModule, MatFormFieldModule, MatInputModule], }) export class InputOverviewExample {}<input>与<textarea>的原生属性支持
matInput对原生属性几乎是"全透传"的。所有可以用于<input>和<textarea>的属性——包括placeholder、maxlength、minlength、pattern、readonly、disabled、name、id等——都可以原样用于<mat-form-field>内部的元素上。同时,Angular 的表单指令(ngModel、formControl、formControlName等)也照常生效。
唯一的限制是type属性只能取matNativeControl所支持的取值(详见下一节)。这一点在源码中体现为type输入属性的 setter 校验逻辑(input.ts):
@Input() get type(): string { return this._type; } set type(value: string) { this._type = value || 'text'; this._validateType(); // 使用 Angular 输入绑定后,开发者无法再直接操作原生元素的属性, // 因此需要将 type 同步回原生属性(textarea 不支持 type 属性) if (!this._isTextarea && getSupportedInputTypes().has(this._type)) { (this._elementRef.nativeElement as HTMLInputElement).type = this._type; } }源码还维护了一个"永不显示为空"的类型集合(input.ts),用于date、datetime、datetime-local、month、time、week这类即使无值也天然非空的控件,且该集合会根据@angular/cdk/platform的getSupportedInputTypes()动态过滤当前浏览器不支持的输入类型。
支持的<input>类型
以下 HTML input 类型 可以与matInput/matNativeControl一起使用:
colordatedatetime-localemailmonthnumberpasswordsearchteltexttimeurlweek
与之相对,源码 input.ts 明确列出了一批不支持的类型,一旦设置会抛出MatInputUnsupportedTypeError:
const MAT_INPUT_INVALID_TYPES = [ 'button', 'checkbox', 'file', 'hidden', 'image', 'radio', 'range', 'reset', 'submit', ];错误消息由 input-errors.ts 生成:
export function getMatInputUnsupportedTypeError(type: string): Error { return Error(`Input type "${type}" isn't supported by matInput.`); }如果你确实需要file、checkbox这类不受支持的类型与<mat-form-field>搭配,文档建议为其编写自定义表单字段控件。
与 Angular Forms 的集成
matInput与@angular/forms完全兼容,支持FormsModule(模板驱动表单)与ReactiveFormsModule(响应式表单)。从源码构造函数看(input.ts),MatInput会注入NgForm、FormGroupDirective(父级表单)以及NgControl(控件本身),并基于这些信息构建错误状态跟踪器:
const parentForm = inject(NgForm, {optional: true}); const parentFormGroup = inject(FormGroupDirective, {optional: true}); const defaultErrorStateMatcher = inject(ErrorStateMatcher);同时它通过MAT_INPUT_VALUE_ACCESSOR注入值访问器(Value Accessor),支持传统回调式访问器与基于 Signal 的访问器(isSignal(value)判断后分别存入_inputValueAccessor或_signalBasedValueAccessor)。当没有显式提供访问器时,直接以原生元素本身作为值访问器:
// 如果未显式指定输入值访问器,则将元素本身作为输入值访问器。 this._inputValueAccessor = element;此外,ngDoCheck(input.ts)在每个变更检测周期内做了三件关键工作,保证了表单状态的健壮性:
- 重新计算错误状态——因为存在无法订阅的错误触发源(例如父表单的提交事件);
- 同步
ngControl.disabled到指令的disabled——statusChanges在emitEvents: false时不会触发,必须显式比对同步; - 对原生元素的
value与placeholder做"脏检查"——覆盖"未使用表单"或"以emitEvent: false更新值"等收不到通知的场景。
与 Signal 表单(signal forms)的兼容
@angular/forms/signals提供的新一代 Signal 表单同样受支持。构造函数中注入了FORM_FIELDtoken(inject(FORM_FIELD, {optional: true, self: true})),当值访问器是 Signal 时,通过 Angulareffect()订阅值变化并触发stateChanges:
if (this._signalBasedValueAccessor) { effect(() => { // 读取值以注册依赖 this._signalBasedValueAccessor!.value(); this.stateChanges.next(); }); }表单字段(mat-form-field)特性一览
任何matInput或matNativeControl元素都可以享用<mat-form-field>提供的全套特性:
- 错误消息(mat-error):基于控件的校验状态显示;
- 提示文本(mat-hint):输入框下方的补充说明;
- 前缀与后缀(matPrefix / matSuffix):输入框内的图标或文字附件;
- 主题化(theming):跟随 Material 主题体系自动换肤;
- 浮动标签(floating label):标签在聚焦/有值时的浮动动画。
这些特性的完整说明参见 form-field 目录 的文档与源码。<mat-form-field>通过MatFormFieldControl接口与MatInput通信,而MatInput暴露了focused、errorState、stateChanges、controlType、autofilled等成员(input.ts),这正是表单字段能够渲染浮动标签、切换错误样式的数据来源。
占位符(Placeholder)
占位符是当<mat-form-field>的标签处于浮动状态且输入为空时显示的文字,用于给用户额外的输入提示。通过设置<input>或<textarea>的placeholder属性即可指定:
<mat-form-field> <mat-label>Email</mat-label> <input matInput type="email" placeholder="Ex. pat@example.com"> </mat-form-field>在某些场景下,<mat-form-field>会把占位符当作标签使用(详见 form-field 的浮动标签文档)。从源码看,MatInput会在ngDoCheck中主动"脏检查"占位符属性(input.ts),因为占位符是否存在取决于一次查询,而这种查询容易触发 "changed after checked" 错误,必须自行同步:
// 我们需要自行脏检查并设置 placeholder 属性, // 因为它是否存在取决于一次容易触发 "changed after checked" 错误的查询。 this._dirtyCheckPlaceholder();自定义错误消息的显示时机:ErrorStateMatcher
默认情况下,<mat-form-field>关联的错误消息在控件无效且用户已与元素交互(touched),或父表单已提交时显示。如果你想改变这一默认行为(例如在控件刚变为 dirty 时就显示错误,或在父表单组 invalid 时立即显示),可以使用matNativeControl的errorStateMatcher属性,其值是一个ErrorStateMatcher实例。
ErrorStateMatcher定义在 error-options.ts:
export class ErrorStateMatcher { isErrorState(control: AbstractControl | null, form: FormGroupDirective | NgForm | null): boolean { return !!(control && control.invalid && (control.touched || (form && form.submitted))); } isSignalErrorState?(field: Field<unknown> | null): boolean { if (!field) { return false; } const invalid = field().invalid(); const touched = field().touched(); return invalid && touched; } }响应式表单(reactive forms)下的自定义 matcher
对于响应式表单,自定义ErrorStateMatcher必须实现isErrorState方法:它接收该控件的FormControl以及父表单(FormGroupDirective或NgForm),返回一个布尔值表示是否显示错误(true显示,false不显示)。
仓库中的 input-error-state-matcher-example.ts 给出了完整范例:
import {FormControl, FormGroupDirective, NgForm, Validators, FormsModule, ReactiveFormsModule} from '@angular/forms'; import {ErrorStateMatcher} from '@angular/material/core'; import {MatInputModule} from '@angular/material/input'; import {MatFormFieldModule} from '@angular/material/form-field'; /** 当无效控件 dirty、touched 或被提交时显示错误。 */ export class MyErrorStateMatcher implements ErrorStateMatcher { isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { const isSubmitted = form && form.submitted; return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted)); } } @Component({ selector: 'input-error-state-matcher-example', templateUrl: './input-error-state-matcher-example.html', imports: [FormsModule, MatFormFieldModule, MatInputModule, ReactiveFormsModule], }) export class InputErrorStateMatcherExample { emailFormControl = new FormControl('', [Validators.required, Validators.email]); matcher = new MyErrorStateMatcher(); }对应的模板(input-error-state-matcher-example.html):
<form class="example-form"> <mat-form-field class="example-full-width"> <mat-label>Email</mat-label> <input type="email" matInput [formControl]="emailFormControl" [errorStateMatcher]="matcher" placeholder="Ex. pat@example.com"> <mat-hint>Errors appear instantly!</mat-hint> @if (emailFormControl.hasError('email') && !emailFormControl.hasError('required')) { <mat-error>Please enter a valid email address</mat-error> } @if (emailFormControl.hasError('required')) { <mat-error>Email is <strong>required</strong></mat-error> } </mat-form-field> </form>Signal 表单(signal forms)下的自定义 matcher
对于 Signal 表单,ErrorStateMatcher仍需实现isErrorState方法以保持与响应式表单 API 的向后兼容,但其实现可以简化为isErrorState() { return false; }。真正的判断逻辑放在新的isSignalErrorState方法中:它接收该matNativeControl对应的Field以及父表单,同样返回布尔值表示是否显示错误。
注意isSignalErrorState在基类中是可选方法(带?标记),且ShowOnDirtyErrorStateMatcher已提供了 Signal 版本的实现(见下文),说明该方法是 Signal 表单时代的扩展点。
全局错误状态 matcher
通过设置ErrorStateMatcherprovider,可以为所有输入框指定全局的错误显示策略。为方便起见,仓库内置了ShowOnDirtyErrorStateMatcher,它会让输入错误在控件 dirty 且 invalid时全局显示(error-options.ts):
@Injectable({providedIn: 'root'}) export class ShowOnDirtyErrorStateMatcher implements ErrorStateMatcher { isErrorState(control: AbstractControl | null, form: FormGroupDirective | NgForm | null): boolean { return !!(control && control.invalid && (control.dirty || (form && form.submitted))); } isSignalErrorState(field: Field<unknown> | null): boolean { if (!field) { return false; } const invalid = field().invalid(); const dirty = field().dirty(); return invalid && dirty; } }在应用启动时全局启用:
bootstrapApplication(MyApp, { providers: [ {provide: ErrorStateMatcher, useClass: ShowOnDirtyErrorStateMatcher} ] });MatInput在构造函数中通过inject(ErrorStateMatcher)获取默认 matcher(input.ts),并在ngDoCheck中调用updateErrorState()刷新状态;同时它也接受组件级/控件级的[errorStateMatcher]输入覆盖默认值(input.ts),二者通过_ErrorStateTracker统一协调。
自动缩放
<textarea>可以通过 CDK 提供的cdkTextareaAutosize指令自动调整高度。引入CdkTextareaAutosize后:
<mat-form-field> <mat-label>Description</mat-label> <textarea matInput cdkTextareaAutosize cdkAutosizeMinRows="2" cdkAutosizeMaxRows="5"></textarea> </mat-form-field>cdkAutosizeMinRows/cdkAutosizeMaxRows控制缩放的上下限。底层实现位于 src/cdk/text-field/autosize.ts,它会在内容变化、窗口缩放等时机重新测量并同步rows与样式高度。
监听输入框的自动填充(autofill)状态
CDK 提供了监听<input>自动填充状态的工具(AutofillMonitor),可用于检测浏览器自动填充发生的时间点并改变填充态外观。MatInput内部已经在使用它(input.ts):
ngAfterViewInit() { if (this._platform.isBrowser) { this._autofillMonitor.monitor(this._elementRef.nativeElement).subscribe(event => { this.autofilled = event.isAutofilled; this.stateChanges.next(); }); } }指令实例上的autofilled属性(实现自MatFormFieldControl)会随事件更新,表单字段可据此调整样式(例如移除自动填充带来的浏览器默认背景色)。销毁时调用stopMonitoring清理订阅(input.ts)。
可访问性(Accessibility)
matNativeControl指令与原生<input>协同工作,提供开箱即用的无障碍体验。其无障碍实现贯穿在宿主绑定与源码中:
Aria 属性
- 如果外层
<mat-form-field>有mat-label标签,它会自动作为<input>的aria-label; - 如果表单字段未指定标签,则应自行添加
aria-label、aria-labelledby或<label for=...>; - 源码在
ngDoCheck/宿主绑定中自动同步aria-required、aria-invalid(input.ts)与aria-disabled(当disabledInteractive为真时输出"true"),其中aria-invalid在"输入为空且必填"时特意输出null以避免与aria-required冗余。
错误与提示(Errors and hints)
- 任何
mat-error和mat-hint都会自动加入输入框的aria-describedby列表; aria-invalid会根据输入框的有效性状态自动更新;- 指令还提供了
@Input('aria-describedby') userAriaDescribedBy(input.ts),允许开发者追加自定义的aria-describedby引用。
需要注意的是:在传达错误信息时不要仅依赖颜色。消息本身应使用图标或"Error:"等文字来表明这是一条错误信息,以确保色觉障碍用户也能识别。
常见问题排查(Troubleshooting)
报错:Input type "..." isn't supported by matInput
当你把输入的type属性设置为matInput指令不支持的值时,会抛出该错误。此错误由 input-errors.ts 的getMatInputUnsupportedTypeError生成,触发点在typesetter 中的_validateType()(input.ts)。
需要用到不受支持的类型(如file、checkbox、range、radio等)时,文档给出的官方建议是:为该输入编写一个自定义表单字段控件,使其实现MatFormFieldControl接口并接入<mat-form-field>的渲染与状态管理。
进阶配置:MAT_INPUT_CONFIG 与 disabledInteractive
除了文档主体内容,仓库源码还提供了一些便于全局配置的扩展点(input.ts):
/** 可用于配置输入框默认选项的对象。 */ export interface MatInputConfig { /** 禁用状态的输入框是否仍然可交互(可聚焦、可复制内容)。 */ disabledInteractive?: boolean; } export const MAT_INPUT_CONFIG = new InjectionToken<MatInputConfig>('MAT_INPUT_CONFIG');通过MAT_INPUT_CONFIG可以全局开启disabledInteractive(此时宿主绑定中disabled被替换为disabled && !disabledInteractive,并输出aria-disabled="true",见 input.ts)。该特性对无障碍与可复制性很有价值:默认禁用输入框不可交互,但某些场景(如代码示例展示)希望内容仍可选中复制。
bootstrapApplication(MyApp, { providers: [ {provide: MAT_INPUT_CONFIG, useValue: {disabledInteractive: true}} ] });总结
matInput/matNativeControl是 Angular Material 表单体系中最常用的入口之一:它以指令形式把原生输入元素接入<mat-form-field>的完整能力栈,同时在源码层面精心处理了属性同步、错误状态跟踪(ErrorStateTracker)、Signal 表单兼容、自动填充监听、无障碍属性同步等底层细节。掌握本文所述的 13 种受支持输入类型、ErrorStateMatcher的两种表单实现方式、全局 provider 配置以及MAT_INPUT_CONFIG扩展点,你就能在真实项目中构建出体验一致、无障碍达标、行为可控的 Material 表单控件。
【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/components
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考