Search code examples
angularformarray

How can I create an array of forms in angular?


I want to create and bind a formControl for each element of the array in score

I tried to do it but I get console error :
Cannot find control with path: 'crime_types -> 0
Cannot find control with path: 'crime_types -> 1 -> score'

 public resultsForm: FormGroup;

 constructor(
    private _formBuilder: FormBuilder,
  ) {
      this.resultsForm = this._formBuilder.group({
      crime_types: this._formBuilder.array([])
    });

     for (let i = 0; i < this.tasks.length; i++) {
      this.formArrayCrimeType.push(
        this._formBuilder.group({
          psychological_tasks_id: new FormControl(this.tasks[i].id),
          patients_id: new FormControl(this.patientId),
          score: new FormControl('', [Validators.required]),
        })
      );
    }
   }

    public get formArrayCrimeType(): FormArray {
    return this.resultsForm.get("crime_types") as FormArray;
  }

    public getNumber(): number {
    this.numb = this.numb + 1;
    return this.numb;
  }
 

Template

<ng-container formArrayName="crime_types">
    <ng-container *ngFor="let task of tasks" [formGroupName]="getNumber()">
        <div *ngIf="task.psychological_processes_id == process.id" class="col-12 col-md-6">
            <div class="form-div">
                <label for="gender" class="form-tag">{{task.description}} *</label>
                <select formControlName="score" class="custom-select" required>
                    <option selected disabled value="">Seleccionar puntaje...</option>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">2</option>
                </select>
            </div>
        </div>
    </ng-container>
</ng-container>

Solution

  • In our case we has a "simple" formArray of FormGroups. The "difficult" is that the formArray is split in tabs, but the "structure" is always the same.

    I wrote comments in the important part of the .html

        <div class="section">
          <div class="section__content container__shadow mx-3">
            <h2 class="section__title">
              {{ test.description }} <span> <i class="bx bx-test-tube"></i></span>
            </h2>
        
            <!--here declare the [formGroup]="resultForms"-->
            <form *ngIf="resultsForm" autocomplete="off" [formGroup]="resultsForm">
        
              <!--and create a div with formArrayName-->
              <div formArrayName="results">
        
                <mat-tab-group mat-align-tabs="center" #matGroup>
                  <mat-tab
                    *ngFor="let process of processes; let i = index"
                    label="{{ process.description }}"
                  >
                    <div class="text-center col-md-12" style="overflow: hidden;">
                      <h2 class="section__subtitle">{{ process.description }}</h2>
                      <div class="row">
                        <ng-container>
        
                          <!--instead iterate over tasks, we iterate 
                              over formArray.controls
                              NOT over tasks (let task of tasks)
                          -->
                          <ng-container
                            *ngFor="let group of formArray.controls; 
                                              let i = index"
                          >
        
                            <!--and we use [formGroupName]="i"
                              I put in another div because we use this div to
                              show or not is "tasks[i]==process.id"
                            -->
                            
                            <div
                              [formGroupName]="i"
                              *ngIf="tasks[i].psychological_processes_id==process.id"
                              class="col-12 col-md-6"
                            >
                              <div class="form-div">
        
                               <!--we use tasks[i].description-->
                                <label for="gender" class="form-tag"
                                  >{{ tasks[i].description }} *</label
                                >
        
                                <!--in select we use formControlName="score"-->
                                <select
                                  class="custom-select"
                                  formControlName="score"
                                  required
                                >
        
                                  <!--see how you indicate that you should
                                      "Seleccionar puntaje", the [value]="null"
                                      makes that works the Validators.required
                                      but when create the formControl we need use
                                score: new FormControl(null, [Validators.required]),
                                  -->
                                  <option selected hidden disabled [value]="null">
                                    Seleccionar puntaje...
                                  </option>
        
                                  <!-- use [ngValue] (not only value) 
                                       if we want to get a number, 
                                       else we get a string-->
                                  <option [ngValue]="1">1</option>
                                  <option [ngValue]="2">2</option>
                                  <option [ngValue]="3">3</option>
                                </select>
                              </div>
                            </div>
                          </ng-container>
                        </ng-container>
                      </div>
                      ...button next...
                      ...button save...
        
                    </div>
                  </mat-tab>
                </mat-tab-group>
              </div>
            </form>
          </div>
        </div>
    

    The forked stackblitz