简体   繁体   中英

FormArray with nested group of controls in Reactive Form with Angular 5

I have been trying create a form in Angular using the reactive method. All seems to be okay until I need to use an FormArray with an FormGroup nested inside. The idea is to get an output that looks like the object below and being able to pass as many items as I want to the items array.

{ 
  'companyName': '',
  'nifcif': '',
  'postcode': '',
  'invoiceNo': 0,
  'invoiceDate': 00/00/00,
  'items': [{'itemQty' : 0, 'itemName' : 0, 'itemPrice' : 0}]
 }

So basically all the form works fine until except by the items part. I get this error mesaje

Cannot find control with name: 'itemQty'

I have to somehow make the control name for each item be vinculated to its index inside the array (I guess) but I tried different approaches with no success. Could you please advice? Thanks in advance!!


Here is my TS file (I didn't bother o create a method to pass more items to the array yet):

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, FormArray } from '@angular/forms';

@Component({
  selector: 'app-process-invoice',
  templateUrl: './process-invoice.component.html',
  styleUrls: ['./process-invoice.component.css']
})
export class ProcessInvoiceComponent implements OnInit {

  invoiceForm: FormGroup;

  constructor() {}


  ngOnInit() {
    let items = new FormArray([new FormGroup({ 'itemQty': new FormControl(null), 'itemName': new FormControl(null)})])
    this.invoiceForm = new FormGroup({
      'companyName': new FormControl(null),
      'nifcif': new FormControl(null),
      'postcode': new FormControl(null),
      'invoiceNo': new FormControl(null),
      'invoiceDate': new FormControl(null),
      'items': items
    })



  }

  onSubmit() {
    console.log(this.invoiceForm);
  }

}

And here my html:

<form [formGroup]="invoiceForm"> 
  <div class="container">
    <div class="row">
      <div class="col-md-6">
        <h3>Company Name:</h3><input formControlName="companyName" type="text" class="form-control form-control-sm" required/>
        <p>NIF/CIF:</p><input formControlName="nifcif" type="text" class="form-control form-control-sm" required/>
        <p>Postal Code:</p><input formControlName="postcode" type="text" class="form-control form-control-sm"/>
      </div>
      <div class="col-md-6">
        <p>Invoice No.:</p><input formControlName="invoiceNo" type="number" class="form-control form-control-sm" required/>
        <p>Date:</p><input formControlName="invoiceDate" type="date" class="form-control form-control-sm" required/>
      </div>
    </div>
  </div>
  <div class="container">
    <div class="table-responsive">
      <table class="table table-sm">
        <thead>
          <tr>
            <th>Qty.</th>
            <th>Product</th>
            <th>Price</th>
            <th></th>
            <th></th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td><input formControlName="itemQty" type="number" class="form-control form-control-sm" required/></td>
            <td><input FormControlName="itemName" type="text" class="form-control form-control-sm" required/></td>
            <td><input formControlName="itemPrice" type="number" class="form-control form-control-sm" required/></td>
            <td><button class="btn btn-outline-primary">+</button></td>
            <td><button class="btn btn-outline-danger">-</button></td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
  <div class="container border">
    <div class="row">
      <div class="col-3"><p>SubTotal : £</p><span></span></div>
      <div class="col-3">
        <div class="input-group">
          <div class="input-group-prepend">VAT %</div>
          <input type="number" class="form-control form-control" id="vat" name="vat" />
        </div>
      </div>
      <div class="col-3"><p>Rebate</p></div>
      <div class="col-3"><p>Total</p></div>
    </div>
  </div>
  <div class="container">
    <div class="row">
      <div class="col"><button (click)="onSubmit()" type="submit" class="btn btn-success">Submit</button></div>
      <div class="col"><button (click)="onDiscardInvoice()" class="btn btn-danger">Discard</button></div>
    </div>
  </div>
</form>

I have something similar ... but I'm allowing multiple addresses defined with a FormGroup.

My code looks like this:

Component

ngOnInit(): void {
    this.customerForm = this.fb.group({
        firstName: ['', [Validators.required, Validators.minLength(3)]],
        lastName: ['', [Validators.required, Validators.maxLength(50)]],
        emailGroup: this.fb.group({
            email: ['', [Validators.required, Validators.pattern('[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+')]],
            confirmEmail: ['', Validators.required],
        }, {validator: emailMatcher}),
        phone: '',
        notification: 'email',
        rating: ['', ratingRange(1, 5)],
        sendCatalog: true,
        addresses: this.fb.array([this.buildAddress()])
    });
}

addAddress(): void {
    this.addresses.push(this.buildAddress());
}

buildAddress(): FormGroup {
    return this.fb.group({
            addressType: 'home',
            street1: '',
            street2: '',
            city: '',
            state: '',
            zip: ''
    });
}

Template

                <div formArrayName="addresses" *ngFor="let address of addresses.controls; let i=index">
                    <div [formGroupName]="i">
                        <div class="form-group" >
                            <label class="col-md-2 control-label"
                                   attr.for="{{'addressType1Id' + i}}">Address Type</label>
                            <div class="col-md-8">
                                <label class="radio-inline">
                                    <input type="radio" id="{{'addressType1Id' + i}}" value="home"
                                    formControlName="addressType">Home
                                </label>
                                <label class="radio-inline">
                                    <input type="radio" id="{{'addressType1Id' + i}}" value="work"
                                    formControlName="addressType">Work
                                </label>
                                <label class="radio-inline">
                                    <input type="radio" id="{{'addressType1Id' + i}}" value="other"
                                    formControlName="addressType">Other
                                </label>
                            </div>
                        </div>

                        <div class="form-group">
                            <label class="col-md-2 control-label"
                                   attr.for="{{'street1Id' + i}}">Street Address 1</label>
                            <div class="col-md-8">
                                <input type="text" 
                                    class="form-control" 
                                    id="{{'street1Id' + i}}" 
                                    placeholder="Street address"
                                    formControlName="street1">
                            </div>
                        </div>
                  ...

Notice how I generated unique Id's using: id="{{'street1Id' + i}}" .

Notice also the first two lines of the HTML which define the formArrayName directive and the formGroupName directive.

You can find the complete code in my github here: https://github.com/DeborahK/Angular2-ReactiveForms in the folder Demo-Final.

sorted, I was trying to do the *ngFor in a wrong way.

I only had to include *ngFor="let item in invoiceform.controls.items.controls; let i = index" [formGroupName] = "i"

My mistake was trying to loop through the formArray straight away with items.controls (forggeting about the form as a whole)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM