Angular中使用*ngFor循环表单仅获取单条数据的问题
Angular表单数据收集问题解决方案
问题描述
使用单个HTML Form,需根据房间数量及每间房的成人数量,通过*ngFor循环生成对应数量的表单(例如2间房各有2位成人时,需生成4组表单),目标是将所有表单数据存入数组。但实际操作中,即便填写了所有表单,FormGroup仅能获取到一条表单数据。
问题根源
- 重复渲染的
<form>标签绑定了同一个ravelerDetailsFormGroup,导致表单控件重复绑定,数据被覆盖 - 错误地将
formgroupname用于FormArray类型的customer_detail,FormArray需使用formArrayName指令配合索引绑定 initDos方法复用了全局的salutation、first_name、last_nameFormControl实例,所有表单行共享同一控件,最终只保留最后一次输入- 未在表单中绑定
room_number字段,无法区分不同房间的旅客
修正后的代码
HTML代码
<!-- 只保留一个外层form标签,避免重复绑定 --> <form [formGroup]="ravelerDetails"> <div *ngFor="let item of roomdetails; let roomIndex = index"> <div class="row"> <mat-card> <h2 class="titleHeader">ROOM {{roomIndex+1}}</h2> <!-- 绑定FormArray类型的customer_detail --> <div formArrayName="customer_detail"> <!-- 遍历当前房间的成人,计算全局数组索引 --> <ng-container *ngFor="let adult of item.adult; let adultIndex = index; let globalIndex = roomIndex * item.adult.length + adultIndex"> <!-- 绑定数组中对应的FormGroup --> <div [formGroupName]="globalIndex"> <!-- 隐藏字段存储房间号 --> <input type="hidden" formControlName="room_number" [value]="roomIndex+1"> <mat-form-field appearance="outline" color="primary" class="col-md-2"> <mat-label>Salutation</mat-label> <mat-select formControlName="salutation"> <mat-option value="Mr">Mr.</mat-option> <mat-option value="Mrs">Mrs.</mat-option> </mat-select> </mat-form-field> <mat-form-field appearance="outline" color="primary" class="col-md-5"> <mat-label>First Name</mat-label> <input matInput placeholder="First Name" formControlName="first_name" maxlength="25"> <mat-error *ngIf="ravelerDetails.get('customer_detail')['controls'][globalIndex].get('first_name')?.hasError('required')"> <strong>First Name</strong> is required. </mat-error> </mat-form-field> <mat-form-field appearance="outline" color="primary" class="col-md-5"> <mat-label>Last Name</mat-label> <input matInput placeholder="Last Name" formControlName="last_name" maxlength="25"> <mat-error *ngIf="ravelerDetails.get('customer_detail')['controls'][globalIndex].get('last_name')?.hasError('required')"> <strong>Last Name</strong> is required. </mat-error> </mat-form-field> </div> </ng-container> </div> </mat-card> </div> </div> <!-- 全局字段隐藏控件 --> <div style="display:none"> <input formControlName="checkin_date"> <input formControlName="checkout_date"> <input formControlName="no_adult"> <input formControlName="no_children"> <input formControlName="no_room"> </div> </form>
TypeScript代码
import { FormBuilder, FormGroup, FormArray, Validators } from '@angular/forms'; // 组件其他代码... // 初始化单个旅客表单组,每次创建新控件实例 initDos(roomNumber: string): FormGroup { return this.fb.group({ room_number: [roomNumber], salutation: ['', Validators.required], first_name: ['', Validators.required], last_name: ['', Validators.required], }); } // 初始化整个旅客详情表单 travellDetiles() { const customerDetailsArray = this.fb.array([]); // 按房间遍历,为每个成人生成表单组 this.roomdetails.forEach((room, roomIndex) => { room.adult.forEach(() => { customerDetailsArray.push(this.initDos((roomIndex + 1).toString())); }); }); this.ravelerDetails = this.fb.group({ customer_detail: customerDetailsArray, checkin_date: [this.hotelSearch.checkin_date], checkout_date: [this.hotelSearch.checkout_date], no_adult: [this.hotelSearch.no_adult], no_children: [this.hotelSearch.no_children], no_room: [this.hotelSearch.no_room], }); } // 获取customer_detail表单数组 get dosArray() { return this.ravelerDetails.get('customer_detail') as FormArray; } // 动态添加旅客(需传入对应房间号) dos(roomNumber: string) { this.dosArray.push(this.initDos(roomNumber)); } // 删除指定索引的旅客表单 removeDos(index: number) { this.dosArray.removeAt(index); }
期望输出
{ "no_room": "1", "checkin_date": "2022-11-5", "checkout_date": "2022-11-6", "no_adult": "2", "customer_detail": [ { "room_number": "1", "salutation": "Mr", "first_name": "test", "last_name": "test" }, { "room_number": "1", "salutation": "Mr", "first_name": "test2", "last_name": "test2" } ] }
内容的提问来源于stack exchange,提问作者vinita suthar
相关产品推荐
相关产品推荐

