Angular 13中如何将FormGroup属性绑定到子组件?
问题概述
父组件初始化包含嵌套FormGroup(profile)和两个FormArray(addresses、identifications)的表单组,通过@Input将根表单组传递给UsersProfileComponent子组件后,子组件绑定profile下的firstname控件时触发错误:ERROR Error: Cannot find control with name: 'firstname'。同时需实现addresses传递给UsersAddressComponent、identifications传递给UsersIdentificationComponent的表单绑定。
父组件核心表单代码:
formUsersInformation: FormGroup = this.formBuilder.group({ profile: this.formBuilder.group({ firstname: new FormControl('', [Validators.required, Validators.minLength(2)]), middlename: new FormControl('', [Validators.required, Validators.minLength(2)]), lastname: new FormControl('', [Validators.required, Validators.minLength(2)]), birthdate: new FormControl('', [Validators.required]), civilstatus: new FormControl('', [Validators.required, Validators.minLength(2)]), emailaddress: new FormControl('', [Validators.required, Validators.email]), phonenumber: new FormControl('', [Validators.required, Validators.pattern('^[0-9]+$')]) }), addresses: this.formBuilder.array([ this.formBuilder.group({ idtype: new FormControl('', [Validators.required]), idnumber: new FormControl('', [Validators.required]), idexpiration: new FormControl('', [Validators.required]), idfile: new FormControl('', [Validators.required]), }) ]), identifications: this.formBuilder.array([ this.formBuilder.group({ street: new FormControl('', [Validators.required]), city: new FormControl('', [Validators.required]), state: new FormControl('', [Validators.required]), zipcode: new FormControl('', [Validators.required]) }) ]), });
父组件传递表单给子组件的代码:
<app-users-profile [formUsersProfile]="formUsersInformation" (FormActionEvent)="formsAction($event)" *ngIf="isUsersProfileVisible"> </app-users-profile>
子组件接收输入的代码:
@Input() formUsersProfile: FormGroup = new FormGroup({});
原错误子组件HTML代码:
<div class="row" [formGroup]="formUsersProfile"> <div class="col-md-4 mb-4"> <label for="firstname" class="form-label">First Name</label> <input type="text" class="form-control radius-none" formControlName="firstname" id="firstname"> </div> </div>
错误原因
子组件模板中直接将根表单组formUsersProfile绑定为当前表单上下文,但firstname控件属于根表单组下的嵌套FormGroup(profile),而非根表单组的直接控件,因此formControlName="firstname"无法找到对应控件。
解决方案
1. 修复UsersProfileComponent的嵌套表单绑定
有两种方式实现正确绑定:
方式一:模板中使用formGroupName指定嵌套表单组
修改子组件HTML,在根表单组绑定的容器内,添加formGroupName="profile"的容器,将表单上下文切换到profile嵌套组:
<div class="row" [formGroup]="formUsersProfile"> <!-- 切换到profile嵌套FormGroup --> <div formGroupName="profile"> <div class="col-md-4 mb-4"> <label for="firstname" class="form-label">First Name</label> <input type="text" class="form-control radius-none" formControlName="firstname" id="firstname"> </div> <!-- 其他profile控件(middlename、lastname等)同理放在此容器内 --> </div> </div>
方式二:子组件中定义getter获取嵌套表单组
在子组件类中添加getter,直接获取profile嵌套组,模板中绑定此getter作为表单上下文:
@Input() formUsersProfile: FormGroup = new FormGroup({}); // 获取profile嵌套FormGroup get profileForm(): FormGroup { return this.formUsersProfile.get('profile') as FormGroup; }
修改子组件HTML:
<div class="row" [formGroup]="profileForm"> <div class="col-md-4 mb-4"> <label for="firstname" class="form-label">First Name</label> <input type="text" class="form-control radius-none" formControlName="firstname" id="firstname"> </div> <!-- 其他profile控件直接在此容器内绑定 --> </div>
2. 实现FormArray传递与子组件绑定
步骤1:父组件中定义FormArray的getter
在父组件类中添加getter,方便获取addresses和identifications两个FormArray:
// 获取addresses FormArray get addressesFormArray(): FormArray { return this.formUsersInformation.get('addresses') as FormArray; } // 获取identifications FormArray get identificationsFormArray(): FormArray { return this.formUsersInformation.get('identifications') as FormArray; }
步骤2:父组件传递FormArray给对应子组件
修改父组件HTML,将两个FormArray分别传递给子组件:
<!-- 传递addresses FormArray给UsersAddressComponent --> <app-users-address [addressesFormArray]="addressesFormArray" *ngIf="isUsersProfileVisible"> </app-users-address> <!-- 传递identifications FormArray给UsersIdentificationComponent --> <app-users-identification [identificationsFormArray]="identificationsFormArray" *ngIf="isUsersProfileVisible"> </app-users-identification>
步骤3:子组件接收FormArray并绑定模板
UsersAddressComponent实现
子组件类接收输入:
import { Component, Input } from '@angular/core'; import { FormArray, FormGroup } from '@angular/forms'; @Component({ selector: 'app-users-address', templateUrl: './users-address.component.html', styleUrls: ['./users-address.component.css'] }) export class UsersAddressComponent { @Input() addressesFormArray: FormArray = new FormArray([]); }
子组件HTML模板绑定FormArray:
<!-- 绑定根表单组(确保FormArray上下文正确) --> <div [formGroup]="addressesFormArray.parent as FormGroup"> <!-- 指定FormArray名称 --> <div formArrayName="addresses"> <!-- 循环遍历FormArray中的每个FormGroup --> <div *ngFor="let address of addressesFormArray.controls; let i = index" [formGroupName]="i" class="mb-3"> <div class="col-md-3 mb-2"> <label for="idtype-{{i}}" class="form-label">ID Type</label> <input type="text" class="form-control radius-none" formControlName="idtype" id="idtype-{{i}}"> </div> <div class="col-md-3 mb-2"> <label for="idnumber-{{i}}" class="form-label">ID Number</label> <input type="text" class="form-control radius-none" formControlName="idnumber" id="idnumber-{{i}}"> </div> <!-- 其他address控件(idexpiration、idfile)同理绑定 --> </div> </div> </div>
UsersIdentificationComponent实现
子组件类接收输入:
import { Component, Input } from '@angular/core'; import { FormArray, FormGroup } from '@angular/forms'; @Component({ selector: 'app-users-identification', templateUrl: './users-identification.component.html', styleUrls: ['./users-identification.component.css'] }) export class UsersIdentificationComponent { @Input() identificationsFormArray: FormArray = new FormArray([]); }
子组件HTML模板绑定FormArray:
<div [formGroup]="identificationsFormArray.parent as FormGroup"> <div formArrayName="identifications"> <div *ngFor="let id of identificationsFormArray.controls; let i = index" [formGroupName]="i" class="mb-3"> <div class="col-md-3 mb-2"> <label for="street-{{i}}" class="form-label">Street</label> <input type="text" class="form-control radius-none" formControlName="street" id="street-{{i}}"> </div> <div class="col-md-3 mb-2"> <label for="city-{{i}}" class="form-label">City</label> <input type="text" class="form-control radius-none" formControlName="city" id="city-{{i}}"> </div> <!-- 其他identification控件(state、zipcode)同理绑定 --> </div> </div> </div>
内容的提问来源于stack exchange,提问作者wenreloz

