Angular 2: Accessing data from FormArray
AngularTypescriptAngular2 FormsAngular Problem Overview
I have prepared a from using ReactiveForms provided by angular2/forms. This form has a form array products:
this.checkoutFormGroup = this.fb.group({
selectedNominee: ['', Validators.required],
selectedBank: ['', Validators.required],
products: productFormGroupArray
});
productFormGroupArray is a array of FormGroup Objects.I fetched the controls i.e. FormArray object using this:
this.checkoutFormGroup.get('products')
I am trying to get the element in the products array at index i
. How can this be done without looping through the array?
Edit:
I tried with at(index) method available:
this.checkoutFormGroup.get('products').at(index)
but this is generating an error as:
Property 'at' does not exist on type 'AbstractControl'.
Edit 2: checkoutData and fund are received from server.
this.checkoutData.products.forEach(product => {
this.fundFormGroupArray.push(this.fb.group({
investmentAmount: [this.fund.minInvestment, Validators.required],
selectedSubOption: ['', Validators.required],
}))
});
Angular Solutions
Solution 1 - Angular
Just cast that control to array
var arrayControl = this.checkoutFormGroup.get('products') as FormArray;
and all its features are there
var item = arrayControl.at(index);
Solution 2 - Angular
While casting the AbstractControl to a FormArray before using the at()
method is a way of doing it, I haven't seen anybody pointing out that you can also do it using the get()
method, which requires no casting.
According to Angular's Documentation, the signature of get()
is :
get(path: string | (string | number)[]): AbstractControl | null
Which means you can also access FormArray's controls with it.
Example :
const formGroup = new FormGroup({
list: new FormArray([
new FormControl('first'),
new FormControl('second'),
]),
});
const firstValue = formGroup.get('list.0').value; // Returns 'first'
const secondValue = formGroup.get('list.1').value; // Returns 'second'
This is really useful, when you want to bind a FormControl in the HTML, where you can't cast anything :
<input [formControl]="formGroup.get('list.0')">
Here is a summary of ways of doing it :
const firstControl = listControl.get('list.0');
const firstControl = listControl.get(['list', 0]);
const firstControl = listControl.get('list').get('0'); // You need a string and not a number
const listControl = formGroup.get('list') as FormArray;
const firstControl = listControl.at(0);
Solution 3 - Angular
One liner as an option to the current accepted answer
var item = (<FormArray>this.checkoutFormGroup.get('products')).at(index);
Solution 4 - Angular
// in .ts component file //
getName(i) {
return this.getControls()[i].value.name;
}
getControls() {
return (<FormArray>this.categoryForm.get('categories')).controls;
}
// in reactive form - Template file //
<mat-tab-group formArrayName="categories" class="uk-width-2-3" [selectedIndex]="getControls().length">
<mat-tab
*ngFor="let categoryCtrl of getControls(); let i = index"
[formGroupName]="i"
[label]="getName(i)? getName(i) : 'جديد'"
>
</mat-tab>
</mat-tab-group>
Solution 5 - Angular
Inside the component: use this
(<FormArray>this.checkoutFormGroup.get('products')).at(index).get('yourFormControlName')
And inside DOM(to apply validation on the formcontrol) use it this way:
<form (ngSubmit)="onSubmit()" [formGroup]="checkoutFormGroup">
<div>
<h4 >Add Products Below</h4>
<div formArrayName="products">
<div *ngFor="let subFormGroup of getControls(); let i = index">
<div class="expense__input" [formGroupName]="i">
<div class="input__group">
<label for="Expense Type">Expense Type</label>
<input type="text"
formControlName="productType">
<span *ngIf="this.expenseForm.get('products').at(i).get('products').invalid">Product Type is Required</span>
</div>
</div>
</div>
</div>
</div>