Search code examples
angularangular-reactive-forms

How to add FormArray to FormGroup without declaring , and adding programmatically


I trying to learn to create generic template for form where in passing json, would define the structure for the table rendered.

I am unable to add controls and form array to the fromGroup programmatically without declaring it earlier

https://stackblitz.com/edit/stackblitz-starters-ksvegt?file=src%2Fmain.ts

here is the code, please help me with this issue, and is it the right approach to do it


Solution

  • NOT use setControl else addControl

      createForm(jsonData: JsonForm) {
        this.form1.addControl('header', new FormControl(jsonData.header));
        const controlArray = this.fb.array([]);
        for (let control of jsonData.controls || []) {
          controlArray.push(this.fb.control(control.value));
        }
        this.form1.addControl('controls', controlArray);
      }
    

    Always you have a fromArray use a getter to "reach" it and iterate over it

      get controls()
      {
        return this.form1.get('controls') as any as FormArray
      }
    

    And you use

      <form [formGroup]="form1">
        <div
          formArrayName="controls"
          *ngFor="let control of controls.controls; let i = index"
        >
          <input  [formControlName]="i" />
        </div>
      </form>
    

    Your forked stackblitz

    Well, this solve your code, but I feel that the way to create a formGroup is really create a formGroup, not a formArray

    In this way the value of your formGroup have the structure of your json data

    For this, I add a new property to your JsonFormControls interface, name, that it's the name of the property

      createForm(jsonData: JsonForm) {
    
        //to iterate over an array use forEach instead a for
        (jsonData.controls || []).forEach((control:JsonFormControls,index:number)=>{
          this.form1.addControl(control.name ||'ctrl'+index,
                                new FormControl(control.value))
        })
      }
    

    Well, now "how iterate"?

    It's not a good idea iterate over jsonData.controls else you can iterate over form.controls using keyvaluePipe

    <ng-template #DEFAULT_FORM_TITILE let-form="form1">
      <h2>{{ jsonData.header }}</h2>
      <form [formGroup]="form1">
    
       <!-see you iterate over form1.controls|keyvalue
          you use let i=index, to get the value of the jsonData.controls[i]
          -->
        <div *ngFor="let control of form1.controls|keyvalue;let i=index">
    
         <!--sorry the "!", it's to avoid errors of "object can be undefined"-->
         <label>{{jsonData!.controls![i].label}}
    
          <input [type]="jsonData!.controls![i]?.type || null" 
                 [formControlName]="jsonData!.controls![i]?.name || null" />
          </label>
        </div>
      </form>
    
      <!--just for check-->
      <pre>
      {{form1.value|json}}
      </pre>
    </ng-template>
    

    A new stackblitz