可重用子组件中的 Angular Reactive Forms:FormArrays 的问题:`formGroupName` 必须与父 formGroup 指令一起使用 [英] Angular Reactive Forms in Reusable Child Components: Problem with FormArrays: `formGroupName` must be used with a parent formGroup directive
问题描述
我试图用一个例子来描述这个问题(关于stackblitz的完整例子)
如果我尝试将反应式表单的某些部分以简单的formControls"或formGroups"的形式与子组件一起放置,则没有问题.(参见上面 stackblitz 的例子).FormGroupDirective
按预期工作.
但是如果我尝试将 FormArray 放在子组件中,我会因为以下原因遇到麻烦:
<!--错误:必须使用 formGroupName带有父 formGroup 指令.--><div>Reactive-Form 是一个简单的形式:
ngOnInit() {this.newForm = this.fb.group({id: [{value: '4711', disabled: this.idReadOnly}, Validators.required],章节:this.fb.group({名称:['某些章节',Validators.required]}),常见问题解答:this.fb.array(this.faqArray)});}
如上所述,id
和 chapter
没有问题,它们是在自定义子组件中实现的,例如:
在 stackblitz 上的应用程序中,您将看到工作部分ID"和章节".
与 formArray
相同的方法:
应该按我的预期工作,但是 <div [formGroupName]="i">
部分仅在子组件中导致上面已经提到的错误:
错误:必须使用 formGroupName带有父 formGroup 指令.
如果我在原始文件中使用它(定义反应形式的位置),它可以正常工作.
我很困惑,也许我只是忽略了一个简单的问题?有人能帮我吗?整个示例可在此处在线获得:( >> stackblitz)
更新 1:
我已经将@yurzui 的解决方案与
视图提供者:[{提供:ControlContainer,useExisting: FormGroupDirective}]
并且错误Error: formGroupName must be used..
"消失了.之后,我为 formArray 字段集成了自定义组件,但它们无法获取控件值.我想我已经接近解决方案了.那是自定义组件:
import { Component, OnInit, Input } from '@angular/core';进口 {表单控件,表单组指令来自'@angular/forms';@成分({选择器:'fe-input',模板:`<mat-form-field><输入垫输入[占位符]="占位符"[formControl]="控制"[必需]="必需"/><mat-error>这是一个必填字段!</mat-error></mat-form-field>`,样式: []})导出类 FormElementInputComponent 实现 OnInit {//要设置的值:@Input() 控制路径:任意;@Input() 占位符:字符串;@Input() 控制值:任意;@Input() required = false;//这是可重用的控件控件:表单控件;构造函数(私有 fgd:FormGroupDirective){}ngOnInit() {this.control = this.fgd.control.get(this.controlPath) 作为 FormControl;}}
最后是 my-form-array
的模板部分:
模板:`<div fxLayout="列"><div *ngFor="let c of control.controls; index as i"><div [formGroupName]="i">
`,
首先,您的 FormArray 是 FormControls 的 FormArray,其值是一个 Object,而不是 FormGroup 的 FormArray.创建表单时必须更改
this.newForm = this.fb.group({id: [{value: '4711', disabled: this.idReadOnly}, Validators.required],章节:this.fb.group({名称:['某些章节',Validators.required]}),常见问题:this.fb.array(this.faqArray.map(x=>this.fb.group({q:x.q,a:x.a})))});
其次,您可以使用其他方式"来处理 FormGroup 的 FormArray,而不是使用 [formGroupName="i"] else [formGroup]="control".是的
<!--删除这个不起作用的<div *ngFor="let c of control.controls; index as i"><div [formGroupName]="i">....<div>
--><!--使用 [formGroup] --><div *ngFor="let c of control.controls; index as i"><div [formGroup]="c">....
好吧,在 formGroup 中你需要你的 fe-input [controlPath]="'q'" 和 [controlPath]="'a'"
我删除了表单组,因为它也是错误的,尝试使用 [formGroup] 并使用 viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }]
你看到你的分叉的stackblitz
更新如果我们使用
标记formArray
我们可以在 ngOnInit 中更改我们的 my-form-array
this.control = this.fgd.control.get(this.controlPath[0]) 作为 FormArray;
和 .html
<div [formGroup]="c"><fe-input *ngFor="let fields of controlPath.slice(1)"[controlPath]="字段"占位符=问题"[必需]="必需"></fe-input>
I'am trying to describe the problem on an example (full example on stackblitz)
If I try to place some parts of reactive-form in the form of simple "formControls" or "formGroups" withing child-components, there are no problems. (See the example on stackblitz above). FormGroupDirective
works as expected.
But If I try to place a FormArray within a child-component, I get troubles because of:
<div [formGroupName]="i">
<!--
Error: formGroupName must be used
with a parent formGroup directive.
-->
<div>
The Reactive-Form is a simple form:
ngOnInit () {
this.newForm = this.fb.group({
id: [{value: '4711', disabled: this.idReadOnly}, Validators.required],
chapter: this.fb.group({
name: ['Some Chapter', Validators.required]
}),
faq: this.fb.array(this.faqArray)
});
}
As already mentioned above there are no problems with id
and chapter
, they are implemented in custom-child-components, like:
<fe-input
[controlPath]="['id']"
placeholder="Child-FormControl ID"
></fe-input>
<my-form-group
[controlPath]="['chapter', 'name']"
placeholder="Child-FormGroup Chapter"
[required]="true"
>
</my-form-group>
In the App on stackblitz you will see the working parts 'ID' and 'Chapter'.
The same approach with formArray
:
<my-form-array
[controlPath]="['faq']"
placeholder="Child-FormArray FAQ"
[required]="true"
></my-form-array>
should work as expected to my mind but the part <div [formGroupName]="i">
causes only within a child-component the already mentioned error above:
Error: formGroupName must be used
with a parent formGroup directive.
If I use that in the original file (where is the reactive form is defined), it works without problems.
I'm confused, maybe I just overlook a simple problem? Can someone help me? The whole example is available online here: ( >> stackblitz)
UPDATE 1:
I've integrated the solution from @yurzui with
viewProviders: [{
provide: ControlContainer,
useExisting: FormGroupDirective
}]
and the error "Error: formGroupName must be used..
" is disappeared. Afterwards, I've integrated the custom component for formArray fields and they cannot get the control value. I think I'm close to the solution. That's the custom component:
import { Component, OnInit, Input } from '@angular/core';
import {
FormControl,
FormGroupDirective
} from '@angular/forms';
@Component({
selector: 'fe-input',
template: `
<mat-form-field>
<input
matInput
[placeholder]="placeholder"
[formControl]="control"
[required]="required"
/>
<mat-error>
This is a required field!
</mat-error>
</mat-form-field>
`,
styles: [
]
})
export class FormElementInputComponent implements OnInit {
// Values to be set:
@Input() controlPath: any;
@Input() placeholder: string;
@Input() controlValue: any;
@Input() required = false;
// That's the reuseable control
control: FormControl;
constructor(private fgd: FormGroupDirective) {}
ngOnInit() {
this.control = this.fgd.control.get(
this.controlPath
) as FormControl;
}
}
And finally that's the template part of my-form-array
:
template: `
<div fxLayout="column">
<div *ngFor="let c of control.controls; index as i">
<div [formGroupName]="i">
<fe-input
[controlPath]="q"
placeholder="Question"
[required]="required"
></fe-input>
<fe-input
[controlPath]="a"
placeholder="Answer"
[required]="required"
></fe-input>
<div>
</div>
</div>
`,
First, your FormArray is a FormArray of FormControls who get as value an Object, NOT a FormArray of FormGroup. You must change when create the form like
this.newForm = this.fb.group({
id: [{value: '4711', disabled: this.idReadOnly}, Validators.required],
chapter: this.fb.group({
name: ['Some Chapter', Validators.required]
}),
faq: this.fb.array(this.faqArray.map(x=>this.fb.group({
q:x.q,
a:x.a
})))
});
Second, you can use the "other way" to work with FormArray of FormGroup that it's not use [formGroupName="i"] else [formGroup]="control". yes
<!--remove this that not work
<div *ngFor="let c of control.controls; index as i">
<div [formGroupName]="i">
....
<div>
</div>
-->
<!--use [formGroup] -->
<div *ngFor="let c of control.controls; index as i">
<div [formGroup]="c">
....
</div>
</div>
Well, inside the formGroup you need your fe-input [controlPath]="'q'" and [controlPath]="'a'"
I remove the form-group because it's wrong too, try use [formGroup] and use viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }]
You see your forked stackblitz
Update if we mark the formArray using
<my-form-array
[controlPath]="['faq','a','q']"
placeholder="Child-FormArray FAQ"
[required]="true"
></my-form-array>
We can change our my-form-array so, in ngOnInit
this.control = this.fgd.control.get(
this.controlPath[0]
) as FormArray;
And the .html
<div *ngFor="let c of control.controls; index as i">
<div [formGroup]="c">
<fe-input *ngFor="let fields of controlPath.slice(1)"
[controlPath]="fields"
placeholder="Question"
[required]="required"
></fe-input>
</div>
这篇关于可重用子组件中的 Angular Reactive Forms:FormArrays 的问题:`formGroupName` 必须与父 formGroup 指令一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!