簡體   English   中英

可重用子組件中的 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 放在子組件中,我會遇到麻煩,因為:

<div [formGroupName]="i">
  <!--
    Error: formGroupName must be used 
    with a parent formGroup directive.
  -->
<div>

Reactive-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)
    });
  }

如上所述, idchapter沒有問題,它們是在自定義子組件中實現的,例如:

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

在 stackblitz 上的應用程序中,您將看到工作部分“ID”和“章節”。

formArray相同的方法:

<my-form-array 
[controlPath]="['faq']" 
placeholder="Child-FormArray FAQ"
[required]="true"
></my-form-array>

應該按我的預期工作,但是<div [formGroupName]="i">僅在子組件中導致上面已經提到的錯誤:

Error: formGroupName must be used 
with a parent formGroup directive.

如果我在原始文件中使用它(定義反應形式的位置),它可以正常工作。

我很困惑,也許我只是忽略了一個簡單的問題? 有人能幫我嗎? 整個示例可在此處在線獲得:( >> stackblitz )

更新1:

我已經將@yurzui 的解決方案與


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

並且錯誤“ Error: formGroupName must be used.. ”消失了。 之后,我為 formArray 字段集成了自定義組件,但它們無法獲取控件值。 我想我已經接近解決方案了。 那是自定義組件:

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;
  }
}

最后是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>
  `,

首先,你的 FormArray 是一個 FormControls 的 FormArray,它的值是一個 Object,而不是 FormGroup 的 FormArray。 您必須在創建表單時進行更改

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
  })))
}); 

其次,您可以使用“其他方式”來處理 FormGroup 的 FormArray,而不是使用 [formGroupName="i"] else [formGroup]="control"。 是的

<!--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>

好吧,在 formGroup 中,您需要 fe-input [controlPath]="'q'" 和 [controlPath]="'a'"

我刪除了表單組,因為它也是錯誤的,嘗試使用 [formGroup] 並使用 viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }]

你看到你的分叉堆棧閃電戰

如果我們使用標記 formArray 進行更新

<my-form-array 
[controlPath]="['faq','a','q']"
placeholder="Child-FormArray FAQ"
[required]="true"
></my-form-array>

我們可以在 ngOnInit 中更改我們的 my-form-array

this.control = this.fgd.control.get(
      this.controlPath[0]
    ) as FormArray;

和 .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>

重新分叉的stackblitz

因此,在您的子組件中,您必須使用formArrayName="faq" ,您將使用FormGroupDirective從父組件獲取它,而在子組件的 viewProviders 數組中,您必須像這樣使用useExisting

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

在你的 html

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM