【发布时间】:2018-11-16 15:30:30
【问题描述】:
问题
向FormGroup 添加无线电类型输入时,FormControl 的值会立即更新。此外,更改选定的单选按钮不会更新 FormControl 值。
代码
我的代码是一个动态表单系统,它使用服务 (FormService) 来维护一个单例 FormGroup,并根据需要添加和删除输入。
我的所有输入都扩展了一个名为 InputBaseComponent 的基本输入类,它导入 FormService 并为自己创建一个表单控件,并通过服务将其添加到 FormGroup。 InputBaseComponent 还包含所有输入都相同的所有通用功能。
FormService
表单服务是一个非常通用的服务,它使用 getter/setter 函数来规范 FormGroup。该服务由InputBaseComponent 导入,每个单独的输入组件都对其进行了扩展。
import { Injectable } from '@angular/core';
import { FormGroup } from '@angular/forms';
@Injectable()
export class FormService {
// An arry of all the inputs in the form
inputs: any[] = [];
// The form group for this instance of the form service singleton
formGroup: FormGroup = new FormGroup({});
// Adds an input to the array of inputs in the form
addInput( input: any ){
// Push the input to the array if it's not already in the inputs array
if ( this.inputs.indexOf(input) === -1 ) this.inputs.push(input);
// Push the input's form control to the form group if it doesn't already exist
if ( !this.formGroup.contains(input.name) ) this.formGroup.addControl(input.name, input.formControl);
}
// Removes an input from the array of inputs in the form
removeInput( input: any ){
// Remove the input from the array if it exists
this.inputs.filter( arrayInput => arrayInput !== input);
// Remove the control from the form group
if ( this.formGroup.contains(input.name) ) this.formGroup.removeControl(input.name);
}
}
BaseInputComponent
这是所有实际输入组件扩展的基本输入组件。这样做是为了将所有通用功能保存在一个地方,以免为每个输入重复代码。为了简单起见,我在最后截断了所有输入,但相信我,它们就在那里。
// ---------- Imports ---------- //
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { FormControl, Validators, ValidatorFn } from "@angular/forms";
/// ---------- Dependencies ---------- ///
import { FormService } from "../form.service";
// ---------- Components ------- //
@Component({
selector: 'input-base-component-do-not-use-directly',
template: ``
})
export class InputBaseComponent implements OnInit {
/// ---------- Constructor ---------- ///
constructor ( public formService: FormService ){};
/// ---------- Properties ----------- ///
// A reference to this form input's form control
formControl: any;
/// ---------- Methods -------------- ///
// On init set up the form control with the proper validators
ngOnInit() {
// Setup the form control with the proper validators
this.setupFormControl( this.getValidators() );
}
// Creates an array of all of the validator functions that should apply to the input and returns it
getValidators(): ValidatorFn[] {
// Create the validators array
let validators: any[] = [];
// Set the max length validator
if (this.maxLength) validators.push(Validators.maxLength(this.maxLength));
// Set the min length validator
if ( this.minLength ) validators.push(Validators.minLength(this.minLength));
// Set the required validator
if ( this.required ) validators.push(Validators.required);
// Include any custom validators
if ( this.customValidators && this.customValidators.length ) validators = validators.concat(this.customValidators);
// Return the array of validator functions
return validators;
}
// Sets up the form control and adds it to the form service
setupFormControl( validators: ValidatorFn[] ): void {
// Create the form control for this input
if (this.formService.formGroup.contains(this.name)) {
this.formControl = this.formService.formGroup.get(this.name)
} else {
this.formControl = this.getNewFormControl( validators );
this.formService.addInput(this);
}
// Update the parent component whenever this input's value is changed
this.formControl.valueChanges.subscribe( newValue => this.valueChange.emit(newValue));
// Add the input to the form group
this.formService.addInput(this);
}
// Takes an array of validator functions and returns a new form control with all of the proper setup for this input
getNewFormControl( validators: ValidatorFn[] ): FormControl {
// Return a new form control for this form
return new FormControl( this.value || null, { validators: validators, updateOn: this.validateOn } );
}
}
兴趣代码
如果您查看setupFormControl 函数的以下行,您可以看到我们正在检查FormGroup 以查看是否已经存在具有为输入指定名称的FormControl,例如单选按钮和复选框。如果它已经存在,它将使用现有的FormControl 作为输入的表单控件。这样做是为了模仿正确的标记,以便在反应式表单中使用单选按钮和复选框,其中组中的每个单选按钮或复选框都使用相同的表单控件。
// Create the form control for this input
if (this.formService.formGroup.contains(this.name)) {
this.formControl = this.formService.formGroup.get(this.name)
} else {
this.formControl = this.getNewFormControl( validators );
this.formService.addInput(this);
}
实现此代码的标记如下:
<!-- input -->
<input
#input
[formControl]="formControl"
[type]="type"
[name]="name"
[id]="idStr"
[value]="value"
tabindex="-1"
/>
可能的解决方案
如前所述,一旦添加表单控件(或在第一个单选框/复选框之后的控件上重新使用),FormControl 实例的值就会更新为最后添加的单选框或复选框。此外,无论何时更改所选单选或复选框,FormControl 的值都不会更新。
所需的功能
此处所需的功能是FormControl 实例的value 属性将保持为null,直到用户选择单选或复选框。此外,当更改选定的单选或复选框时,FormControl 的值应该更新。
【问题讨论】:
-
您可以删除
[value]="value"并分享您看到的具体行为。 -
该值未按预期更新,但在与单选按钮交互时该值未更新。