How to add an element in nested FormArrays?

691 views Asked by At

I have an array in another array like that:

<div class="col-sm-6">
  <div formArrayName="site_source_pages">
    <div *ngFor="let site_source_page of siteForm.controls.site_source_pages.controls; let x=index">
      <div [formGroupName]="x">
        <div class="card card-accent-success">
          <div class="card-header">
            <strong>Yeni Kaynak Sayfa</strong>
          </div>
          <div class="card-block">
            <div class="row">
              <!--Site Page Source Form Group-->
              <div class="form-group">
                <div class="form-group col-sm-6">
                  <label for="page_url">Sayfa Url</label>
                  <!--<label *ngIf="!site_source_page.controls[x].controls.page_url.valid" class="text-danger">*</label>-->
                  <input type="text" class="form-control" id="page_url" placeholder="Badge-Manager" formControlName="page_url">
                </div>
              </div>
              <div class="form-group col-sm-6">
                <label for="postal-code">Tarama Aralığı</label>
                <input type="text" class="form-control" id="postal-code" placeholder="Badge-Email">
              </div>
            </div>
            <div class="row">
              <div formArrayName="page_containers">
                <div *ngFor="let page_container of site_source_page.controls.page_containers.controls; let i=index">
                  <div [formGroupName]="i">
                    <div class="col-sm-4">
                      <div class="form-group">
                        <label for="ccnumber">Container {{i + 1}} Type</label>
                        <label [hidden]="site_source_page.controls.page_containers.controls[i].controls.container_type.valid" class="text-danger">*</label>
                        <input type="text" class="form-control" id="container_type" placeholder="Container Type" formControlName="container_type">
                      </div>
                    </div>
                    <div class="col-sm-4">
                      <div class="form-group">
                        <label for="ccnumber">Container {{i + 1}} Selector Type</label>
                        <input type="text" class="form-control" id="selector_type" placeholder="Container Selector Type" formControlName="selector_type">
                      </div>
                    </div>
                    <div class="col-sm-4">
                      <div class="form-group">
                        <label for="ccnumber">Container {{i + 1}} Css Query</label>
                        <span class="glyphicon glyphicon-remove pull-right" *ngIf="site_source_page.controls.page_containers.controls.length > 1" (click)="removePageContainer(i)">x</span>
                        <input type="text" class="form-control" id="container_css_query" placeholder="Container Css Query" formControlName="selector_css_query">
                      </div>
                    </div>
                    <div class="col-sm-12">
                      <button type="button" class="btn btn-secondary btn-block" (click)="addPageContainer(x)">Container Ekle</button>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

I want to add item to second array via typescript. I try to reach second array (page_containers) but get this error:

Cannot read property 'controls' of undefined

My function is like that:

addPageContainer(x: number) {
  // add container to the list    
  const controlSp = <FormArray>this.siteForm.controls['site_source_pages'];
  const controlPc = <FormArray>controlSp.controls['page_containers'];
  controlPc.push(this.initPageContainer());
}

Why can't I reach second array with this line?

const controlPc = <FormArray>controlSp.controls['page_containers'];

Any idea?

2

There are 2 answers

0
developer033 On

The problem is that controlSp is a FormArray, so to access page_containers, as any other common array, you have to specify the index.

You can do it using FormArray#at method:

const controlSp = <FormArray>this.siteForm.controls['site_source_pages'];
const controlPc = <FormArray>controlSp.at(x).controls['page_containers'];

or even better using AbstractControl#get method:

addPageContainer(index: number) {
  const controlPc = this.siteForm.get(`site_source_pages.${index}.page_containers`) as FormArray;
  controlPc.push(this.initPageContainer());
}

Also, as you may have noticed I changed the type cast from <variable> to as variable.

Why? Because while the as syntax is fully compatible with JSX/TSX syntax, the other way isn't.

Note that if you don't have any problem with this, you can use whatever you prefer.


That said, as an advice, you can simplify your HTML a lot.

Instead of (lines in order):

*ngFor="let site_source_page of siteForm.controls.site_source_pages.controls"

[hidden]="site_source_page.controls.page_containers.controls[i].controls.container_type.valid

*ngIf="site_source_page.controls.page_containers.controls.length > 1

You can have (lines in order):

*ngFor="let site_source_page of siteForm.get('site_source_pages').controls"

*ngIf="page_container.get('container_type').invalid

*ngIf="site_source_page.get('page_containers').length > 1"

Also, just a minor note:

The following line is actually commented, but it's invalid:

<label *ngIf="!site_source_page.controls[x].controls.page_url.valid" class="text-danger">*</label>

It should be:

<label *ngIf="!site_source_page.controls.page_url.valid" class="text-danger">*</label>

or even better:

<label *ngIf="site_source_page.get('page_url').invalid" class="text-danger">*</label>
0
Govind On

Try to use get method,

controlSp = <FormArray> this.siteForm.get('site_source_pages');

FOr more details you can refer to the Angular document