1

I have the code of a components class below:

import {FormBuilder, FormGroup, Validators, FormControl, FormArray} from '@angular/forms'

...
form: FormGroup
constructor(private _fb: FormBuilder) { }

this.form = this._fb.group({
      name: ['', [Validators.required]],
      sortItem: this._fb.array([
        this.initSort(),
      ])
    });

initSort(){
    return this._fb.group({
      locationName: ['', [Validators.required]],
      locationItems: this._fb.array([
        this.initSortItems()
      ])
    })
  }

initSortItems(){
    return this._fb.group({
      itemName: ['', [Validators.required]],
      itemPicture: ['', []],
    })
  }

addSort(){
    const control = <FormArray>this.form.controls['sortItem'];
    control.push(this.initSort());
  }

addSortLocationItem(i?: number, t?: number){
    // add more locationItems to the sort. This is where I am stuck

// if tried the following with no avail  (<FormArray>this.form.get('sortItem').value[i].controls).push(this.initSortItems());

  }

I am trying to add more locationItems but what I've tried in the addSortLocationItem() method doesn't work. How can I access the form controls of the correct iteration of sortItem and add multiple locations items to that iteration?

This is my markup:

<div class="">
  <a (click)="addSort()" style="cursor: default">
    Add Sort Locations +
  </a>
</div>

<div formArrayName="sortItem">
  <div *ngFor="let sortLocation of form.controls.sortItem.controls; let i=index">
      <!-- address header, show remove button when more than one address available -->
      <div>
          <span>Location {{i + 1}}</span>
          <span *ngIf="form.controls.sortItem.controls.length > 1"
              (click)="removeAddress(i)">
          </span>
      </div>

      <div [formGroupName]="i">
          <!--name-->
          <div>
              <label>Location Name</label>
              <input type="text" formControlName="locationName">
              <!--display error message if street is not valid-->
              <small [hidden]="form.controls.sortItem.controls[i].controls.locationName.valid">
                  Street is required
              </small>
          </div>

          <div formArrayName="locationItems">
            <div *ngFor="let eachItem of form.controls.sortItem.controls[i].controls.locationItems.controls; let t=index">
                <!-- address header, show remove button when more than one address available -->
                <div class="">
                  <a (click)="addSortLocationItem(i,t)" style="cursor: default">
                    Add Items +
                  </a>
                </div>

                <div>
                    <span>Location Item {{t + 1}}</span>
                    <span *ngIf="form.controls.sortItem.controls[i].controls.locationItems.controls[t].length > 1"
                        (click)="removeAddress(t)">
                    </span>
                </div>

                <div [formGroupName]="t">
                    <!--name---->
                    <div>
                        <label>Item Name</label>
                        <input type="text" formControlName="itemName">
                        <!--display error message if street is not valid-->
                        <small [hidden]="form.controls.sortItem.controls[i].controls.locationItems.controls[t].controls.itemName.valid">
                            Name is required
                        </small>
                    </div>
                  </div>
              </div>
          </div>
      </div>
  </div>
</div>

Any help with this will be greatly appreciated.

1 Answer 1

2

If i is the index of the main control, so you can easily get with the following:

addSortLocationItem(i: number, t: number) {
  const control = this.sortItem.get(`${i}.locationItems`) as FormArray;
  control.push(this.initSortItems());
}

And, as an advice, I'd recommend you to create some variables in order to facilitate the readability of your code.

form: FormGroup;
name: FormControl;
sortItem: FormArray;

This:

<div *ngFor="let sortLocation of form.controls.sortItem.controls; let i=index">
...
<div *ngFor="let eachItem of form.controls.sortItem.controls[i].controls.locationItems.controls; let t=index">

Can be replaced by:

<div *ngFor="let sortLocation of sortItem.controls; let i = index">
...
<div *ngFor="let eachItem of sortLocation.get('locationItems').controls; let t = index">

The complete code:

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

@Component({
  ...
})
export class BlaBlaComponent implements OnInit {

  form: FormGroup;
  name: FormControl;
  sortItem: FormArray;

  constructor(private readonly formBuilder: FormBuilder) { }

  ngOnInit(): void {  
    this.name = this.formBuilder.control('', [Validators.required]);
    this.sortItem = this.formBuilder.array([this.initSort()]);

    this.form = this.formBuilder.group({
      name: this.name,
      sortItem: this.sortItem
    });
  }

  initSort() {
    return this.formBuilder.group({
      locationName: ['', [Validators.required]],
      locationItems: this.formBuilder.array([
        this.initSortItems()
      ])
    })
  }

  initSortItems() {
    return this.formBuilder.group({
      itemName: ['', [Validators.required]],
      itemPicture: ['', []],
    })
  }

  addSort() {
    this.sortItem.push(this.initSort());
  }

  addSortLocationItem(i: number, t: number) {
    const control = this.sortItem.get(`${i}.locationItems`) as FormArray;
    control.push(this.initSortItems());
  }
 }

PS: I didn't test, let me know if I forgot something.

Sign up to request clarification or add additional context in comments.

2 Comments

You sir are a genius! Thanks it was driving me crazy. Works as planned, thanks for the shorthand as well on the html portion.
I have a similar question with form arrays but this time in regards to file inputs. Please can you take a look here and see if you can help? stackoverflow.com/questions/41907471/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.