angulartypescriptformarrayformgroups

Formgroups and FormArray


From data that I recive from API I want to create a reactive form. I have a formparent and multiple childforms. The data that I want to display in form looks like this:

    data{
    "extraInformation": false
    "cars": [
    {
     "Id": 48,  
    "Period": { 
    "Start": null, 
    "End": null }, 
    "Rentalstats": null, 
    "Reason": null 
    } 
    ]
    }

How do I achieve this? So far this is what I have done:

this.form = this.formBuilder.group({

    extraInformation: [this.data.ExtraInformation, [Validators.required]],
    cars: this.fb.array([this.addDetailFormGroup()]),
});

addDetailFormGroup() {
  this.form = new FormGroup({
    Reason: new FormControl(this.data?.cars?.Reason, Validators.required),
    Rentalstats: new FormControl(this.data?.cars?.Rentalstats, Validators.required),
    Period: new FormGroup([this.initListFormGroup()]),
  });
  initListFormGroup() {
    return new FormGroup({
      Start: new FormControl(this.data?.cars?.Period?.Start, Validators.required),
      End: new FormControl(this.data?.cars?.Period?.End, Validators.required),
    });
  }

But it dosent work because I get "Cannot read properties of undefined (reading 'Start')..

I would really appreciate it if someone could help


Solution

  • Would say that your code is not compilable as there are a few errors:

    1. You are trying to re-assign the FormGroup instance with the form controls: Reason, Rentalstats, and Period that overwrite the original FormGroup with the form controls: extraInformation and cars array.

    2. From the attached data, this.data?.cars is an array, you can't directly access Reason and other fields, but you need to iterate each element of the cars array, create FormGroup for each element and adding it into the cars FormArray.

    3. Period: new FormGroup([this.initListFormGroup()]), This is not the proper way to assign the FormGroup instance to Period FormGroup. Your current code is trying to assign the value as FormGroup to a FormGroup control.

    4. Incorrect way to declare initListFormGroup method. You should declare the method outside of addDetailFormGroup method.

    The naming of the methods to create FormGroup is unclear/not meaningful, you should name it something like initCarFormArray and initPeriodFormGroup for better readability.

    The component code to create the form and its nested FormArray and `FormGroup(s) should look as below:

    ngOnInit() {
      this.form = this.formBuilder.group({
        extraInformation: [this.data.extraInformation, [Validators.required]],
        cars: this.formBuilder.array([]),
      });
    
      this.initCarFormArray();
    }
    
    initCarFormArray() {
      for (let car of this.data.cars) {
        let carFormGroup = this.formBuilder.group({
          Reason: new FormControl(car?.Reason, Validators.required),
          Rentalstats: new FormControl(car?.Rentalstats, Validators.required),
          Period: this.initPeriodFormGroup(car.Period),
        });
    
        this.cars.push(carFormGroup);
      }
    }
    
    initPeriodFormGroup(period: any) {
      return new FormGroup({
        Start: new FormControl(period?.Start, Validators.required),
        End: new FormControl(period?.End, Validators.required),
      });
    }
    
    get cars(): FormArray {
      return this.form.controls.cars as FormArray;
    }
    

    For the HTML to generate the form:

    <div [formGroup]="form">
      <div>
        <span>ExtraInformation:</span>
        <input formControlName="extraInformation" />
      </div>
    
      <div formArrayName="cars">
        <ng-container *ngFor="let controls of cars.controls; let i = index;">
          <div [formGroupName]="i">
            <div formGroupName="Period">
              <div>
                <span>Start:</span>
                <input formControlName="Start" />
              </div>
    
              <div>
                <span>End:</span>
                <input formControlName="End" />
              </div>
            </div>
    
            <div>
              <span>Rentalstats:</span>
              <input formControlName="Rentalstats" />
            </div>
    
            <div>
              <span>Reason:</span>
              <input formControlName="Reason" />
            </div>
          </div>
        </ng-container>
      </div>
    </div>
    

    Demo @ StackBlitz