Angular 5中子组件接收FormGroup添加验证的报错解决方法
解决Angular子组件无法感知父组件Form上下文的问题
这问题我之前也踩过坑!核心原因是子组件默认不会自动继承父组件的<form>上下文,所以没法直接访问父级的FormGroup或者表单状态。这里给你几个靠谱的解决方案:
方案1:通过@Input直接传递FormGroup(简单直接)
这是最容易上手的方式,把父组件的FormGroup直接传给子组件,子组件拿到后就能给控件添加验证规则:
父组件代码
import { Component, OnInit } from '@angular/core'; import { FormGroup, FormBuilder } from '@angular/forms'; @Component({ selector: 'app-parent', template: ` <form [formGroup]="myForm" (ngSubmit)="onSubmit()"> <app-child [parentForm]="myForm"></app-child> <button type="submit">提交</button> </form> ` }) export class ParentComponent implements OnInit { myForm: FormGroup; constructor(private fb: FormBuilder) {} ngOnInit() { this.myForm = this.fb.group({}); } onSubmit() { console.log(this.myForm.valid); } }
子组件代码
import { Component, Input, OnInit } from '@angular/core'; import { FormGroup, Validators } from '@angular/forms'; @Component({ selector: 'app-child', template: ` <input formControlName="childInput" placeholder="子组件输入框"> <div *ngIf="parentForm.get('childInput').invalid && parentForm.get('childInput').touched"> 输入不能为空! </div> ` }) export class ChildComponent implements OnInit { @Input() parentForm: FormGroup; ngOnInit() { // 给父FormGroup添加子控件和验证规则 this.parentForm.addControl('childInput', this.parentForm.control('', Validators.required)); } }
方案2:实现ControlValueAccessor接口(官方推荐,更优雅)
这种方式能让子组件成为一个自定义表单控件,自动集成到父表单的上下文中,不用手动传递FormGroup,扩展性更强,适合需要复用的组件:
子组件代码
import { Component, forwardRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, Validators, FormControl } from '@angular/forms'; @Component({ selector: 'app-child', template: ` <input [formControl]="childControl" placeholder="子组件输入框"> <div *ngIf="childControl.invalid && childControl.touched"> 输入不能为空! </div> `, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ChildComponent), multi: true } ] }) export class ChildComponent implements ControlValueAccessor { childControl = new FormControl('', Validators.required); // ControlValueAccessor接口必填实现 onChange: (value: any) => void = () => {}; onTouched: () => void = () => {}; writeValue(value: any): void { this.childControl.setValue(value); } registerOnChange(fn: any): void { this.onChange = fn; this.childControl.valueChanges.subscribe(fn); } registerOnTouched(fn: any): void { this.onTouched = fn; this.childControl.statusChanges.subscribe(status => { if (status.includes('touched')) { fn(); } }); } setDisabledState(isDisabled: boolean): void { isDisabled ? this.childControl.disable() : this.childControl.enable(); } }
父组件使用方式
// 父组件ts this.myForm = this.fb.group({ childInput: ['', Validators.required] // 父级也可添加验证,会和子组件验证合并 }); // 父组件模板 <form [formGroup]="myForm" (ngSubmit)="onSubmit()"> <app-child formControlName="childInput"></app-child> <button type="submit">提交</button> </form>
方案3:注入NgForm获取父表单上下文(适合模板驱动表单)
如果你的项目用的是模板驱动表单,可以直接在子组件中注入NgForm,获取父表单的实例:
子组件代码
import { Component, OnInit, Inject } from '@angular/core'; import { NgForm, Validators } from '@angular/forms'; @Component({ selector: 'app-child', template: ` <input name="childInput" ngModel placeholder="子组件输入框"> <div *ngIf="ngForm.form.get('childInput').invalid && ngForm.form.get('childInput').touched"> 输入不能为空! </div> ` }) export class ChildComponent implements OnInit { constructor(@Inject(NgForm) private ngForm: NgForm) {} ngOnInit() { // 给父表单控件添加验证 const control = this.ngForm.form.get('childInput'); if (control) { control.setValidators(Validators.required); control.updateValueAndValidity(); } } }
选择建议
- 响应式表单优先选方案2,符合Angular官方规范,组件复用性更高;
- 简单场景快速实现选方案1;
- 模板驱动表单场景选方案3。
亲测这几种方式都能解决子组件无法感知父表单上下文的问题,你可以根据项目实际情况来选!
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

