Angular模板驱动表单中自定义组件包裹input无法被识别问题
问题原因
Angular 模板驱动表单的 NgForm 默认只会收集直接放在其作用域下、同时声明了name属性和[(ngModel)]绑定的表单控件,自定义组件内部的输入控件属于组件内部作用域,外层表单无法直接感知到,所以不会被采集。
解决方案
需要让自定义表单组件实现 Angular 提供的 ControlValueAccessor 接口,同时注册 NG_VALUE_ACCESSOR 提供商,让外层表单将自定义组件识别为合法的表单控件。
第一步:修改 custom-textbox.component.ts
import { Component, Input, forwardRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; @Component({ selector: 'app-custom-textbox', templateUrl: './custom-textbox.component.html', // 注册表单控件提供商 providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CustomTextboxComponent), multi: true } ] }) export class CustomTextboxComponent implements ControlValueAccessor { // 内部绑定的输入值 value: string = ''; // 禁用状态 disabled: boolean = false; // 表单值变化回调 onChange = (val: string) => {}; // 控件被触碰回调 onTouched = () => {}; // 实现ControlValueAccessor接口方法:表单给控件赋值时调用 writeValue(val: string): void { this.value = val ?? ''; } // 实现ControlValueAccessor接口方法:注册值变化回调 registerOnChange(fn: any): void { this.onChange = fn; } // 实现ControlValueAccessor接口方法:注册触碰回调 registerOnTouched(fn: any): void { this.onTouched = fn; } // 实现ControlValueAccessor接口方法:设置控件禁用状态 setDisabledState?(isDisabled: boolean): void { this.disabled = isDisabled; } // 内部输入框值变化时同步给表单 handleInputChange(val: string): void { this.value = val; this.onChange(val); this.onTouched(); } }
第二步:修改 custom-textbox.component.html
<p>My custom textbox component</p> <input type="text" [disabled]="disabled" [ngModel]="value" (ngModelChange)="handleInputChange($event)" />
第三步:修改外层 app.component.html 调用方式
<div ngForm #myForm="ngForm"> <!-- 直接给自定义组件加name和ngModel绑定,和原生input用法一致 --> <app-custom-textbox name="wrappedInput" [(ngModel)]="wrappedInputValue" ></app-custom-textbox> <hr /> <input name="noWrapper" [(ngModel)]="message" /> </div> <hr /> <p>myForm.value is below:</p> {{ myForm.value | json }}
修改完成后,myForm.value 就会同时包含wrappedInput和noWrapper两个属性了。
内容的提问来源于stack exchange,提问作者roman m
相关产品推荐
相关产品推荐

