Angular中模板引用mat-form-field触发MatFormFieldControl缺失错误
Angular复用mat-select模板触发mat-form-field错误的原因与解决办法
错误原因
mat-form-field在初始化过程中会主动查找内部的MatFormFieldControl(比如mat-select、mat-input这类符合规范的控件),完成关联后才能正常工作。但使用ngTemplateOutlet复用模板时,mat-select是通过模板插入到mat-form-field内部的,而mat-form-field的初始化时机早于模板的DOM插入时机——当mat-form-field完成初始化时,内部还没有mat-select的DOM节点,因此检测不到对应的控件,就会抛出mat-form-field must contain a MatFormFieldControl错误。
无需重复编写mat-select代码的解决办法
方案1:通过*ngIf延迟mat-form-field的渲染
让mat-form-field在模板内容准备好后再渲染,确保初始化时能找到内部的mat-select:
<!-- 带mat-form-field的场景 --> <ng-container *ngIf="useFormField"> <mat-form-field> <ng-container *ngTemplateOutlet="selectTemplate"></ng-container> </mat-form-field> </ng-container> <!-- 不带mat-form-field的场景 --> <ng-container *ngIf="!useFormField"> <ng-container *ngTemplateOutlet="selectTemplate"></ng-container> </ng-container> <!-- 复用的mat-select模板 --> <ng-template #selectTemplate> <mat-select [(value)]="selectedOption"> <mat-option value="opt1">选项1</mat-option> <mat-option value="opt2">选项2</mat-option> <mat-option value="opt3">选项3</mat-option> </mat-select> </ng-template>
通过*ngIf控制mat-form-field的渲染时机,保证它初始化时,内部的mat-select已经被模板插入到DOM中,这样就能正常检测到控件。
方案2:手动关联控件与mat-form-field
通过ViewChild获取mat-form-field和mat-select实例,在视图初始化完成后手动完成关联:
组件代码:
import { Component, ViewChild, AfterViewInit } from '@angular/core'; import { MatFormField } from '@angular/material/form-field'; import { MatSelect } from '@angular/material/select'; @Component({ selector: 'app-reusable-select', templateUrl: './reusable-select.component.html' }) export class ReusableSelectComponent implements AfterViewInit { selectedOption: string; useFormField = true; @ViewChild('formField') private formField: MatFormField; @ViewChild('select') private select: MatSelect; ngAfterViewInit(): void { if (this.formField && this.select) { // 手动将mat-select绑定为mat-form-field的控件 this.formField._control = this.select; // 监听控件状态变化,同步更新mat-form-field的状态 this.select.stateChanges.subscribe(() => { this.formField.updateControlType(); }); } } }
模板代码:
<mat-form-field #formField *ngIf="useFormField"> <ng-container *ngTemplateOutlet="selectTemplate"></ng-container> </mat-form-field> <ng-container *ngIf="!useFormField"> <ng-container *ngTemplateOutlet="selectTemplate"></ng-container> </ng-container> <ng-template #selectTemplate> <mat-select #select [(value)]="selectedOption"> <mat-option value="opt1">选项1</mat-option> <mat-option value="opt2">选项2</mat-option> <mat-option value="opt3">选项3</mat-option> </mat-select> </ng-template>
⚠️ 注意:该方案用到了mat-form-field的私有属性_control,虽然能解决问题,但Angular Material版本更新时可能会对私有属性做调整,需要注意版本兼容性。
内容的提问来源于stack exchange,提问作者Toni Kanoni
相关产品推荐
相关产品推荐

