FormGroup inside FormArray in Angular 2 Reactive Forms

FormGroup inside FormArray in Angular 2 Reactive Forms

我未能使 Angular 2 响应式工作,它有一个 FormGroup 嵌套在 FormArray 中。有人可以告诉我我的设置有什么问题吗?

为简洁起见,省略了代码中不相关的部分。

以下是我的组件

orderForm: FormGroup = this.fb.group({
    id: [''],
    store: ['', Validators.required],

    //The part related to the error
    order_lines: this.fb.array([
        this.fb.group({
           id: [''],
           order_id: [],
           product_id: [],
           description: ['', Validators.required],
           unit_price: ['', Validators.required],
           discount: [0],
           units: [1, Validators.required],
           line_total: ['', Validators.required]
        })
    ])
});

constructor(private fb: FormBuilder) {  }

//Order instance is passed from elsewhere in the code
select(order: Order): void {
    this.orderForm.reset(order)
}

传递给select方法的Order实例是这样的:

  {
        "id": 20,
        "store": "Some Store",
        "order_lines": [
            {
                "id": 6,
                "order_id": 20,
                "product_id": 1,
                "description": "TU001: Polka dots",
                "unit_price": "1000.00",
                "discount": "100.00",
                "units": 2,
                "line_total": "1900.00"
            },
            {
                "id": 7,
                "order_id": 20,
                "product_id": 2,
                "description": "TU002: Polka dots",
                "unit_price": "500.00",
                "discount": "0.00",
                "units": 1,
                "line_total": "500.00"
            }
        ]
    }

视图模板如下。

<form [formGroup]="orderForm">
   <input type="number" formControlName="id">
   <input type="text" formControlName="store">

   <div formArrayName="order_lines">
      <div *ngFor="let line of orderForm.get('order_lines'); let i=index">
          <div [formGroupName]="i">
               <input type="text" [formControlName]="product_id">
               <input type="text" [formControlName]="description">
               <input type="number" [formControlName]="units">
               <input type="number" [formControlName]="unit_price">
               <input type="number" [formControlName]="line_total">
          </div>
      </div>
   </div>
</form>

此设置给我一个控制台错误 **无法在 order_lines -> 0 -> ** 找到控件。我想知道我做错了什么。

我可以通过 order_lines FormArray 中的一个简单的 FormControl 使其工作。但是当在 FormArray.

中使用 FormGroup 时,它会因给定错误而失败

你能帮我解决这个问题吗?

与其使用 form.get 不如使用如下所示的控件 -

<form [formGroup]="orderForm">
   <input type="number" formControlName="id">
   <input type="text" formControlName="store">

   <div formArrayName="order_lines">
      <div *ngFor="let line of orderForm.controls.order_lines.controls; let i=index">
          <div [formGroupName]="i">
               <input type="text" formControlName="product_id">
               <input type="text" formControlName="description">
               <input type="number" formControlName="units">
               <input type="number" formControlName="unit_price">
               <input type="number" formControlName="line_total">
          </div>
      </div>
   </div>
</form>

Working Example

你错过了什么 -

  • 您在 *ngFor 的最后一个中缺少 .control 来迭代控件。
  • 如果您使用上述方法,您需要将 [formControlName] 替换为 formControlName

您可以移动 formArrayName 与 *ngFor 内联,并在 ngFor 中包含 .controls,因为我们正在循环表单控件。

<div>
      <div formArrayName="order_lines" *ngFor="let line of orderForm.get('order_lines').controls; let i=index">
          <div [formGroupName]="i">
               <input type="text" [formControlName]="product_id">
               <input type="text" [formControlName]="description">
               <input type="number" [formControlName]="units">
               <input type="number" [formControlName]="unit_price">
               <input type="number" [formControlName]="line_total">
          </div>
      </div>
   </div>

@Johna,补充我的回答:

你有两个函数

  buildForm(data:any):FormGroup
  {
    return data?this.fb.group({
      id: [data.id?data.id:''],
      store: [data.store?data.store:'', Validators.required],
      order_lines:this.fb.array(this.buildArrayControl(data.order_lines?data.order_lines:null))
    })
    :
    this.fb.group({
      id: [''],
      store: ['', Validators.required],
      order_lines:this.fb.array(this.buildArrayControl(null))
    })

  }

  buildArrayControl(data:any[]|null):FormGroup[]
  {
    return data?
    data.map(x=>{
      return this.fb.group({
           id: [x.id?x.id:''],
           order_id: [x.order_id?x.order_id:''],
           product_id: [x.product_id?x.product_id:''],
           description: [x.description?x.description:'', Validators.required],
           unit_price: [x.unit_price?x.unit_price:'', Validators.required],
           discount: [x.discount?x.discount:0],
           units: [x.units?x.units:1, Validators.required],
           line_total: [x.line_total?x.line_total:'', Validators.required]
        }) 
    })
    :
    [this.fb.group({
           id: [''],
           order_id: [],
           product_id: [],
           description: ['', Validators.required],
           unit_price: ['', Validators.required],
           discount: [0],
           units: [1, Validators.required],
           line_total: ['', Validators.required]
        }) 
    ]
  }

那么你可以这样做,例如

this.orderForm = this.buildForm(
    {
      id:'112',
      store:'22655',
      order_lines:[{description:1222,....},{description:1455,...}]
    }
  )

 //or 
 this.orderForm=this.buildForm(null);

我知道 OP 的问题已经得到解答,但我想 post 我的问题的解决方案,由于 OP 的问题和以上答案,我解决了这个问题。

我被要求创建一个动态表单(问卷),但在实现嵌套 Angular 动态表单时遇到了一些问题。

生成的动态表单可以预先填充多个条目,或者您可以向空列表中添加更多字段,或者两者兼而有之。

我的用例创建了一个汽车检查表,起始数组中的每一项都是操作员的 'TO-DO' 任务。

Angular 9 + Material + Bootstrap.

下面的代码会更清楚:

/* Component Typescript */

constructor( private formBuilder: FormBuilder ) {  }

arrayTitoli = [
    "TASK1",
    "TASK2",
    "TASK3",
    "TASK4",
    "TASK5",
  ]
  
  addTaskField(value) {
  
    this.array.push(
        
      this.formBuilder.group({
        titolo: value,
        checkbox: false,
        noteAggiuntive: ""
      })

    )
  }

  get titoliArray(){
    
    let array = [];

    for(let i = 0;i <this.arrayTitoli.length;i++){
      array.push(
        
        this.formBuilder.group({
          titolo: this.arrayTitoli[i],
          checkbox: false,
          noteAggiuntive: ""
        })

      )
    }

    return array;
  }

  angForm: FormGroup = this.formBuilder.group({
  
    array: this.formBuilder.array(this.titoliArray)

  });

  get array(): FormArray {
    return this.angForm.get('array') as FormArray;
  }

  
  onFormSubmit(): void {
    console.log(this.angForm.value)
    
  }
<!-- Component HTML -->

<div class="p-2">
<form [formGroup] = "angForm" (ngSubmit)="onFormSubmit()">
    <div>
        <button type="submit" class="btn btn-primary">Send</button>
      </div>
    <div formArrayName="array">
        <div *ngFor="let line of array.controls; index as idx;">
            <div [formGroupName]="idx">
            <mat-grid-list  style="text-align: center!important;" [cols]="3" rowHeight="13vh" (window:resize)="onResize($event)">
                
                <mat-grid-tile style="font-weight: bold;font-size: smaller;" class="col-md-4 col-xs-4" colspan="1">
                    {{line.controls['titolo'].value}} 
                 </mat-grid-tile>

                 <mat-grid-tile class="col-md-4" colspan="1">
                    <mat-checkbox formControlName="checkbox" #check color="primary">
                        <!--<button *ngIf="!mobile"  type="button" (click)="check.toggle()" class="btn btn-{{check.checked ? 'success' : 'danger'}} btn-sm" >SPUNTA</button>-->
                    </mat-checkbox>
                    
            
                    <mat-form-field  [style.maxWidth.%]="mobile ? '70' : '100'" style="margin-left: 10%;">
                        <mat-label *ngIf="!mobile">Note aggiuntive</mat-label>
                        <mat-label *ngIf="mobile">Note</mat-label>
                        <textarea formControlName="noteAggiuntive" matInput value='' cdkTextareaAutosize #autosize="cdkTextareaAutosize" cdkAutosizeMinRows="1"
                            cdkAutosizeMaxRows="5"></textarea>
                    </mat-form-field>
                </mat-grid-tile>

            </mat-grid-list>
            <hr>
        
        </div>
    </div>
    </div>
      
      
</form>
<div>
    <mat-form-field>
    <input type="text" #newTask matInput>
    </mat-form-field>
    <span class="px-2">
        <button type="button" class="btn btn-primary" (click)="addTaskField(newTask.value)">Create</button>
    </span>
  </div>
</div>

希望有用。