Angular 2: Accessing data from FormArray

AngularTypescriptAngular2 Forms

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSumit AgarwalView Question on Stackoverflow
Solution 1 - AngularRadim KöhlerView Answer on Stackoverflow
Solution 2 - AngularTyphonView Answer on Stackoverflow
Solution 3 - Angularfg78ncView Answer on Stackoverflow
Solution 4 - AngularIssa LafiView Answer on Stackoverflow
Solution 5 - AngularTadele AyelegnView Answer on Stackoverflow