angular 4 将包含其他对象的对象传递给 FormBuilder.group 函数会导致奇怪的表单行为
angular 4 passing an object containing other objects into FormBuilder.group function leads to a strange form behavior
正在创建由以下内容组成的嵌套表单 "nested object"(建筑物):
export class Building{
id: number = 0;
doorsCount: number = 0;
description: string = '';
address: Address = new Address();
buildingType: BuildingType = new BuildingType();
}
export class Address{
id: number = 0;
description: string = '';
}
export class BuildingType{
id: number = 0;
description: string = '';
}
如您所见,Building class 包含其他 classes,例如 Address 和 BuildingType,它们还具有其他属性,例如 id 和 description。
创建表单时,我在组件 ts 文件中使用了以下代码:
buildingForm: FormGroup;
construct(private fb: FormBuilder){
this.buildingForm = this.createBuildingFG(new Building);
}
createBuildingFG(building: Building){
let formGroup : FormGroup;
formGroup = this.fb.group(building);
// Because object of type "building" contain properties of non-primitive
// types such as object Address and BuildingType I think the following
// additional lines are required.
formGroup.controls.address = this.fb.group(building.address);
formGroup.controls.buildingType = this.fb.group(building.buildingType);
return formGroup;
}
这就是表单绑定到 HTML 模板的方式:
<form [formGroup]="buildingForm">
<label>
Door count:
<input formControlName="doorsCount" >
</label>
<label>
Building description:
<input formControlName="description" >
</label>
<label formGroupName="address">
Address:
<input formControlName="description" >
</label>
<label formGroupName="buildingType">
Building type:
<input formControlName="description" >
</label>
</form>
现在,当我输出整个表单的值时,问题就出现了,这些值并没有真正根据在 formGroup 内的嵌套字段控件中键入的内容进行更新,例如 address 或 buildingType。否则正常更新。
值是这样输出的
<div>
{{buildingForm.value | json}}
</div>
但是,如果 createBuildingFG 函数的执行方式不同,并且每个 formControl 都是显式创建的,而不仅仅是传递整个对象,则表单会正常运行。示例:
createBuildingFG(building: Building){
let formGroup : FormGroup;
formGroup = this.fb.group({
doorsCount: '',
description: '',
address: this.fb.group({ description: ''}),
buildingType: this.fb.group({ description: ''})
});
return formGroup;
}
谁能解释一下这是怎么回事?显然,为了避免显式定义 fromGroup 的每个元素这一繁琐的任务,人们可能只想传递整个对象。
正如评论中提到的@jonrsharpe
because the initial construction does things you're not subsequently
overriding.
那么这些东西是什么?
当 angular 创建 FormGroup 的新实例时,它调用 _setUpControls
方法
export class FormGroup extends AbstractControl {
constructor(
public controls: {[key: string]: AbstractControl},
validatorOrOpts?: ValidatorFn|ValidatorFn[]|AbstractControlOptions|null,
asyncValidator?: AsyncValidatorFn|AsyncValidatorFn[]|null) {
super(
coerceToValidator(validatorOrOpts),
coerceToAsyncValidator(asyncValidator, validatorOrOpts));
this._initObservables();
this._setUpdateStrategy(validatorOrOpts);
this._setUpControls(); <--------------
现在让我们看看方法:
/** @internal */
_setUpControls(): void {
this._forEachChild((control: AbstractControl) => {
control.setParent(this);
control._registerOnCollectionChange(this._onCollectionChange);
});
}
正如我们所见,每个控件项都设置了父项并注册了一些事件,但您没有这样做。
以下代码应该有效:
formGroup = this.fb.group(building);
formGroup.controls.address = formGroup.controls.buildingType = null;
formGroup.registerControl('address', this.fb.group(building.address));
formGroup.registerControl('buildingType', this.fb.group(building.buildingType));
或者您可以使用递归来实现它:
constructor(private fb: FormBuilder){
this.buildingForm = this.createFormGroup(new Building);
}
createFormGroup(obj: any) {
let formGroup: { [id: string]: AbstractControl; } = {};
Object.keys(obj).forEach(key => {
formGroup[key] = obj[key] instanceof Object ? this.createFormGroup(obj[key]) : new FormControl(obj[key]);
});
return this.fb.group(formGroup);
}
正在创建由以下内容组成的嵌套表单 "nested object"(建筑物):
export class Building{
id: number = 0;
doorsCount: number = 0;
description: string = '';
address: Address = new Address();
buildingType: BuildingType = new BuildingType();
}
export class Address{
id: number = 0;
description: string = '';
}
export class BuildingType{
id: number = 0;
description: string = '';
}
如您所见,Building class 包含其他 classes,例如 Address 和 BuildingType,它们还具有其他属性,例如 id 和 description。
创建表单时,我在组件 ts 文件中使用了以下代码:
buildingForm: FormGroup;
construct(private fb: FormBuilder){
this.buildingForm = this.createBuildingFG(new Building);
}
createBuildingFG(building: Building){
let formGroup : FormGroup;
formGroup = this.fb.group(building);
// Because object of type "building" contain properties of non-primitive
// types such as object Address and BuildingType I think the following
// additional lines are required.
formGroup.controls.address = this.fb.group(building.address);
formGroup.controls.buildingType = this.fb.group(building.buildingType);
return formGroup;
}
这就是表单绑定到 HTML 模板的方式:
<form [formGroup]="buildingForm">
<label>
Door count:
<input formControlName="doorsCount" >
</label>
<label>
Building description:
<input formControlName="description" >
</label>
<label formGroupName="address">
Address:
<input formControlName="description" >
</label>
<label formGroupName="buildingType">
Building type:
<input formControlName="description" >
</label>
</form>
现在,当我输出整个表单的值时,问题就出现了,这些值并没有真正根据在 formGroup 内的嵌套字段控件中键入的内容进行更新,例如 address 或 buildingType。否则正常更新。
值是这样输出的
<div>
{{buildingForm.value | json}}
</div>
但是,如果 createBuildingFG 函数的执行方式不同,并且每个 formControl 都是显式创建的,而不仅仅是传递整个对象,则表单会正常运行。示例:
createBuildingFG(building: Building){
let formGroup : FormGroup;
formGroup = this.fb.group({
doorsCount: '',
description: '',
address: this.fb.group({ description: ''}),
buildingType: this.fb.group({ description: ''})
});
return formGroup;
}
谁能解释一下这是怎么回事?显然,为了避免显式定义 fromGroup 的每个元素这一繁琐的任务,人们可能只想传递整个对象。
正如评论中提到的@jonrsharpe
because the initial construction does things you're not subsequently overriding.
那么这些东西是什么?
当 angular 创建 FormGroup 的新实例时,它调用 _setUpControls
方法
export class FormGroup extends AbstractControl {
constructor(
public controls: {[key: string]: AbstractControl},
validatorOrOpts?: ValidatorFn|ValidatorFn[]|AbstractControlOptions|null,
asyncValidator?: AsyncValidatorFn|AsyncValidatorFn[]|null) {
super(
coerceToValidator(validatorOrOpts),
coerceToAsyncValidator(asyncValidator, validatorOrOpts));
this._initObservables();
this._setUpdateStrategy(validatorOrOpts);
this._setUpControls(); <--------------
现在让我们看看方法:
/** @internal */
_setUpControls(): void {
this._forEachChild((control: AbstractControl) => {
control.setParent(this);
control._registerOnCollectionChange(this._onCollectionChange);
});
}
正如我们所见,每个控件项都设置了父项并注册了一些事件,但您没有这样做。
以下代码应该有效:
formGroup = this.fb.group(building);
formGroup.controls.address = formGroup.controls.buildingType = null;
formGroup.registerControl('address', this.fb.group(building.address));
formGroup.registerControl('buildingType', this.fb.group(building.buildingType));
或者您可以使用递归来实现它:
constructor(private fb: FormBuilder){
this.buildingForm = this.createFormGroup(new Building);
}
createFormGroup(obj: any) {
let formGroup: { [id: string]: AbstractControl; } = {};
Object.keys(obj).forEach(key => {
formGroup[key] = obj[key] instanceof Object ? this.createFormGroup(obj[key]) : new FormControl(obj[key]);
});
return this.fb.group(formGroup);
}