C0mpl3x
C0mpl3x

Reputation: 532

How to add array of objects to angular form

Let's say I have a json with these values:

{
car: bmw,
tunes : [{
 name: turbo,
 cost: 350
},
{
 name: exhaust,
 cost: 100
}]
}

How do I create a form that will be able to take these values? FormGroup won't work I think. FormArray seems like the right thing to use but I haven't seen it being used with pre selected fields that can be assigned values to, which is what I need. I need to be able to assign array of object to the from, edit certain values of the objects and export the form as a json with the array of objects (same as the array came but maybe with different values). The fields wont change so I don't need to add them dynamically, I just need to assign them.

My imagined solution would look something like this:

form: New FormGroup({
 car: new FormControl(),
 tunes: new FormArray([
  name: new FormControl(),
  cost: new FormControl()
 ])
})

Update:

This seems to be the wanted solution:

form: New FormGroup({
     car: new FormControl(),
     tunes: new FormArray([
      new FormGroup({
       name: new FormControl(),
       cost: new FormControl()
      })
     ])
    })

but it gives me error: "Cannot find form control at index 1"

When I look at the json it produces, it is the wanted outcome but I'm not able to assign values right now.

Upvotes: 0

Views: 2521

Answers (1)

Devang Patel
Devang Patel

Reputation: 1843

I think this is what you are looking for:

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

export interface ITune {
  name: string;
  cost: string;
}

export interface Icar {
  car: string;
  tunes: ITune[];
}

@Component({
  selector: 'list-overview-example',
  templateUrl: 'list-overview-example.html'
})

export class ListOverviewExample implements OnInit {

  form: FormGroup;
  public validator: { [key: string]: ValidatorFn | null };
  public data: Icar;

  constructor(private fb: FormBuilder) {
    this.validator = {
      name: Validators.compose([Validators.required, Validators.maxLength(50)])
    };
    this.data = {
      car: 'bmw',
      tunes: [
        { name: 'turbo', cost: '100$' },
        { name: 'exhaust', cost: '200$' }
      ]
    };
  }

  public ngOnInit() {
    this.form = this.createFormGroup(this.fb, this.data, {});
    this.form.addControl(
      'tunes',
      new FormArray(this.addTunes(this.data.tunes))
    );
  }

  public addTunes(tunes: ITune[]): FormGroup[] {
    const formGroups: FormGroup[] = [];
    tunes.forEach(x => {
      const formGroup = this.createFormGroup(this.fb, x, this.validator);
      formGroups.push(formGroup);
    });
    return formGroups;
  }

  createFormGroup<T extends { [key: string]: any }>(
    formBuilder: FormBuilder,
    dataSource: T,
    validators: { [key: string]: ValidatorFn | null }
  ): FormGroup {
    // dynamic creation of form control by enumerating the object
    const config: any = {};
    Object.keys(dataSource).forEach((propertyName: string) => {
      const fieldVlidators = validators[propertyName];
      const value = dataSource[propertyName];
      if (!(value instanceof Array)) {
        if (fieldVlidators !== undefined) {
          config[propertyName] = [value, fieldVlidators];
        } else if (fieldVlidators !== undefined) {
          config[propertyName] = [value, fieldVlidators];
        } else {
          config[propertyName] = [value];
        }
      }
    });
    const group = formBuilder.group(config);
    return group;
  }
}

You can use .addControl() method to add control on the formGroup. createFormGroup() is a generalized method that you can use to create the formgroup. You can also pass the validator in that if you need to validate any control. You just need to pass the object with name and validator rule in createFormGroup() function.

Upvotes: 3

Related Questions