Reputation: 65
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>
Upvotes: 1
Views: 8201
Reputation: 65
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)
Upvotes: 1
Reputation: 60518
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.
Upvotes: 5