Angular动态表单复制后Radio按钮无唯一Name问题求助
问题:Angular动态表单中Radio按钮全局互斥,无法区段独立选择
我开发的Angular表单里,每个动态生成的区段都包含一组Radio按钮,但当前同一时间只能选中所有区段中的一个Radio按钮,推测是这些Radio按钮的name属性不唯一导致的。
表单核心代码
get anatomicalComplaints(): FormArray { return this.childForm.get('dcrr_pc')?.get('pc_anat_loc_compl') as FormArray } addAnatomicalComplaints() { this.anatomicalComplaints.push(this.createNewAnatomicalComplaint()) } removeAnatomicalComplaints(index: number) { this.anatomicalComplaints.removeAt(index) } createNewAnatomicalComplaint() { return new FormGroup({ spinal: new FormControl(''), vas: new FormControl(''), laterality: new FormControl(''), radicular_component: new FormControl(''), pc_cas_hist_doc_compl: new FormControl('') }) }
自定义app-form-input组件代码
TypeScript代码
import { CommonModule } from '@angular/common'; import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, FormGroupDirective, AbstractControl, FormArray } from '@angular/forms'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'app-form-input', standalone: true, imports: [CommonModule], templateUrl: './form-input.component.html', styleUrl: './form-input.component.scss', providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => FormInputComponent), multi: true }, ], viewProviders: [ {provide: ControlContainer, useExisting: FormGroupDirective} ] }) export class FormInputComponent implements OnInit, ControlValueAccessor, OnDestroy { @Input() placeholder: string = '' @Input() type: string = 'text' @Input() label: string = '' @Input() required: boolean = false @Input() minLength: number = 0 @Input() labelClassList: string = '' @Input() classList: string= '' @Input() size: string = 'sm' @Input() options: string[] = []; @Input() selectOptions: any; @Input() controlName : string | null = null @Input() indexAt : number = 0 @Input() formType : string = 'horizontal' @Input() inputGroup : boolean = false protected onTouched: any = () => {} private onChange: any = () => {} private destroy$ = new Subject<void>() control !: AbstractControl value: any; constructor(public parentForm: FormGroupDirective) {} ngOnInit() { if (this.controlName && this.parentForm.form) { // 获取FormArray对应索引的FormGroup中的目标控件 const formArray = this.parentForm.form.get('dcrr_pc.pc_anat_loc_compl') as FormArray; this.control = formArray.at(this.indexAt).get(this.controlName)!; // 同步控件值到组件 this.control.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(val => { this.value = val; }); } } setRadioId = (index: number) => `${this.controlName}${this.indexAt}${index}` writeValue(value: any): void { this.value = value } registerOnChange(fn: any): void { this.onChange = fn } registerOnTouched(fn: any): void { this.onTouched = fn } onInputChange(event: any): void { this.value = event.target.value this.onChange(this.value); // 更新对应FormControl的值 if(this.control) { this.control.setValue(this.value); } } ngOnDestroy(): void { this.destroy$.next() this.destroy$.complete() } }
组件模板代码
@if(!inputGroup) { <div [class]="classList"> @if(label != '' && formType == 'default') { <label>{{label}}</label> } @if(type == 'text' || type == 'date') { <input [type]="type" [placeholder]="placeholder" [value]="value" (input)="onInputChange($event)" (blur)="onTouched()" class="form-control" [class.form-control-sm]="size == 'sm'" /> } @if(type == 'radio') { @for(option of options; track option; let idx = $index) { <div class="form-check form-check-inline"> <input class="form-check-input" [type]="type" [name]="controlName + indexAt" [id]="setRadioId(idx)" [value]="option" [checked]="value === option" (change)="onInputChange($event)"> <label class="form-check-label" [for]="setRadioId(idx)"> {{option}} </label> </div> } } @if(type == 'checkbox') { @for(option of options; track option; let idx = $index) { <div class="form-check"> <input [type]="type" class="form-check-input" [name]="controlName + indexAt"> <label class="form-check-label"> {{option}} </label> </div> } } @if(type == 'select') { <select class="form-control" [class.form-control-sm]="size == 'sm'" (input)="onInputChange($event)" (blur)="onTouched()"> <option value="">--Payer--</option> <option *ngFor="let o of selectOptions" [value]="o">{{o}}</option> </select> } @if(type == 'textarea') { <textarea class="form-control" [class.form-control-sm]="size == 'sm'" rows="5" width="100%"></textarea> } </div> } @if(inputGroup) { <div [class]="classList"> <div class="input-group"> <span class="input-group-text border-0 bg-transparent">{{label}}</span> @if(type == 'text' || type == 'date') { <input [type]="type" [placeholder]="placeholder" [value]="value" (input)="onInputChange($event)" (blur)="onTouched()" class="form-control" [class.form-control-sm]="size == 'sm'" /> } @if(type == 'select') { <select class="form-control" [class.form-control-sm]="size == 'sm'" (input)="onInputChange($event)" (blur)="onTouched()"> <option value="">--Payer--</option> <option *ngFor="let o of selectOptions" [value]="o">{{o}}</option> </select> } </div> </div> }
尝试过的方案
- 传入唯一索引
- 在自定义组件内设置formControlName
- 配置唯一ID
以上方案均未解决问题。
复现步骤
- 加载应用
- 点击蓝色加号按钮多次添加区段
- 尝试选择Radio按钮
结果对比
- 当前实际结果:只能选中全局一个Radio按钮
- 期望结果:每个区段内可独立选择Radio按钮
解决方案
问题根源是所有区段的Radio按钮使用了相同的name属性,导致浏览器将它们视为同一组,从而全局互斥;同时组件未正确与FormArray中对应索引的FormControl绑定,值同步异常。
生成唯一Radio组name:将模板中Radio输入框的
[name]="controlName"修改为[name]="controlName + indexAt",让每个区段的Radio组拥有唯一名称,浏览器会将它们识别为独立组。绑定对应FormControl:在组件
ngOnInit中,根据indexAt和controlName从FormArray的对应FormGroup中获取目标控件,订阅其值变化同步到组件;在onInputChange中更新控件值,确保表单数据双向同步。同步Radio选中状态:给Radio输入框添加
[checked]="value === option",保证选中状态与FormControl的值一致。
内容的提问来源于stack exchange,提问作者Mark Hill
相关产品推荐
相关产品推荐

