Angular Reactive Forms in Reusable Child Components: Problem with FormArrays: `formGroupName` must be used with a parent formGroup directive

核能气质少年 提交于 2021-02-06 13:50:52

问题


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>
  `,


回答1:


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>

the re-forked stackblitz




回答2:


so in your child component you have to use formArrayName="faq" which you will be getting from parent component using FormGroupDirective and in child component's viewProviders array you have to use useExisting like this

viewProviders: [
    { provide: ControlContainer, useExisting: FormGroupDirective }
  ]

in your html

<div formArrayName="faq" *ngFor="let fa of faq['controls']; let faqIndex = index;">
    <div [formGroupName]="faqIndex">
// here goes your code
     </div>
</div>



来源:https://stackoverflow.com/questions/56842459/angular-reactive-forms-in-reusable-child-components-problem-with-formarrays-f

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!