How can I generate FormControls dynamically inside a FormGroup?

Tags: , , , ,



I am receiving an object from the db that contains a few elements with the following format:

info = [{
    idcentro: "8227",
    namecentro: "Centro Paris", 
    address: "C/ Paris, 127",
    dias: [
      {
        dia: "0",
        horafinal: "06:00",
        horainicio: "17:00",
        salas: [
          {
            id: 0,
            nombre: "sala 1",
            intervalos: [
                ["09:00", "12:30", "10"],
                ["13:00", "21:30", "20"]
            ]
          }
        ]
      },
      {
        dia: "1",
        horafinal: "09:00",
        horainicio: "16:30",
        salas: [
          {
            id: 0,
            nombre: "sala 1",
            intervalos: [
                ["09:00", "12:30", "10"]
            ]
          },
          {
            id: 1,
            nombre: "sala 2",
            intervalos: [
                ["09:00", "20:30", "20"]
            ]
          }
        ]
      },
      {
        dia: "2",
        horafinal: "09:00",
        horainicio: "20:30",
        salas: [
          {
            id: 0,
            nombre: "sala 1",
            intervalos: [
                ["09:00", "12:30", "10"],
                ["12:45", "18:30", "15"]
            ]
          },
          {
            id: 1,
            nombre: "sala 2",
            intervalos: [
                ["09:00", "20:30", "20"]
            ]
          }
        ]
      }
    ]
  }];

To work with those “dias” array and include inputs to modify each value in case the user wants to, I’ve manually created a FormGroup containing a formControl for each of those days (the real object contains up to 8 days).

    this.openingHoursForm = new FormGroup({
      day0OpeningTime: new FormControl(""),
      day0ClosingTime: new FormControl(""),
      day1OpeningTime: new FormControl(""),
      day1ClosingTime: new FormControl(""),
      day2OpeningTime: new FormControl(""),
      day2ClosingTime: new FormControl(""),
      day3OpeningTime: new FormControl(""),
      day3ClosingTime: new FormControl(""),
      day4OpeningTime: new FormControl(""),
      day4ClosingTime: new FormControl(""),
      day5OpeningTime: new FormControl(""),
      day5ClosingTime: new FormControl(""),
      day6OpeningTime: new FormControl(""),
      day6ClosingTime: new FormControl(""),
      day7OpeningTime: new FormControl(""),
      day7ClosingTime: new FormControl(""),
      day8OpeningTime: new FormControl(""),
      day8ClosingTime: new FormControl("")
    });

And then, in the HTML, I am creating two inputs for each of those those formControlNames:

<div class="dayOpeningHours" title="">
  <div>Day 0</div>
   <div class="opens">
     <label for="">De:
       <input class="form-control" type="time" formControlName="day0OpeningTime" name="day0OpeningTime">
     </label>
   </div>
   <div class="closes">
     <label for="">A:
       <input class="form-control" formControlName="day0ClosingTime" name="day0ClosingTime">
     </label>
   </div>
</div>

As you can see, this is becoming very unmaintainable. I have tried to create the markup (in the html) and formControlNames (in the ts) dynamically by looping through each “dia” of the ‘dias’ array, but I haven’t achieved anything.

Is there a way I can generate the HTML and the declaration of those Form Controls inside the same formGroup? I have been reading about FormArray and AbstractControl but I can not really understand how to apply that to my code.

Can someone help me out or give me some tips to find the right documentation?

Thank you

Answer

The better option would be to use a FormArray instead. You can also inject FormBuilder from @angular/forms

See Below

constructor (private fb: FormBuilder) { }
diasForm = this.fb.group({
  openingHours: this.fb.array([])
})
get openingHours (): FormArray {
  return this.diasForm.get('openingHours') as FormArray;
}

The next task will be to create the form Lets assume we have dias as an array

dias = [
      {
        dia: "0",
        horafinal: "06:00",
        horainicio: "17:00",
      },
      {
        dia: "1",
        horafinal: "06:00",
        horainicio: "17:00",
      },
      ...
]

I have removed other properties for simplicity In the ngOnInit we can set the value of our form

ngOnInit() {
  this.dias.forEach(dia => {
    this.openingHours.push(
      this.fb.group({
        horafinal: [dia.horafinal, []],
        horainicio: [dia.horainicio, []]
      })
    )
  })
}

In the Html

<form [formGroup]='diasForm'>
  <div formArrayName='openingHours'>
    <div *ngFor='let item of openingHours.controls; let i = index' 
       [formGroupName]='i'>
         <input formControlName='horafinal' />
         <input formControlName='horainicio' />
    </div>
  </div>
</form>

The above should match the structure of our formGroup

diasForm( [formGroup] ) ---> openingHours(formArray) ---> i ( [formGroup] ) ---> horafinal(formControl)

Please see this demo on stackblitz

This way you can have a dynamic number of days



Source: stackoverflow