vishnu
vishnu

Reputation: 4599

Dynamically adding form elements using formGroup in Angular reactive forms

I have form fields and need to add some more different fields when the user clicks on the Add button.If the user clicks on Add button, the new form is opening in a modal window where he can configure the name of the field, type and value). How do i add newly created dynamic elements to existing form.

I have tried using the following code but not adding the newly generated fields.
issuer.html

<dynamic-form [dataObject]="person"></dynamic-form>
<button type="submit" class="btn btn-info" (click)="addbtn();">ADD</button>


issuer.component.ts

import { Component, OnInit } from '@angular/core';
@Component({
  selector: 'issuer-config',
  templateUrl: './issuer.html'
})


export class IssuerConfig implements OnInit {
  person: any = [];

  constructor( 

  ) {
    person = {
    lcid: {
        label: 'LCID:',
        value: '',
        type: 'text'
    }
   }
  }
 //Assume the values coming from modal window
  addbtn = function() {
    Object.assign(this.person, {
      ipa: {
        label: 'IP:',
        value: '',
        type: 'text'
    },
  });

  };
}

dynamicform.component.ts

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

@Component({
    selector: 'dynamic-form',
    template: `
    <form novalidate (ngSubmit)="onSubmit(form.value)" [formGroup]="issuerConfigForm" class="form-horizontal">
      <div *ngFor="let prop of objectProps">
      <div class="form-group row">
        <label [attr.for]="prop" class="col-sm-2 control-label">{{prop.label}}</label>

        <div [ngSwitch]="prop.type" class="col-sm-4">
                <input *ngSwitchCase="'text'" 
                    [formControlName]="prop.key"
                    [id]="prop.key" [type]="prop.type" class="form-control">
        </div>
          </div>
      </div> 
            <button type="submit">Submit</button>
    </form>
  `
})
export class DynamicFormComponent implements OnInit {
    @Input() dataObject;
    issuerConfigForm: FormGroup;
    objectProps;

    constructor() {
    }

    ngOnInit() {
        // remap the API to be suitable for iterating over it
        this.objectProps =
            Object.keys(this.dataObject)
                .map(prop => {
                    return Object.assign({}, { key: prop }, this.dataObject[prop]);
                });

        // setup the form
        const formGroup = {};
        for (let prop of Object.keys(this.dataObject)) {
            formGroup[prop] = new FormControl(this.dataObject[prop].value || '');
        }
        this.issuerConfigForm = new FormGroup(formGroup);
    }
    onSubmit(form) {
        console.log(form);
    }
}

Upvotes: 0

Views: 3831

Answers (2)

Indrakumara
Indrakumara

Reputation: 1645

issuer.html

<dynamic-form [objectProps]="objectProps" [formGroup]="issuerConfigForm"></dynamic-form>
<button type="submit" class="btn btn-info" (click)="addbtn();">ADD</button>

issuer.component.ts

    export class IssuerConfig {
  ngOnInit() {

  }
  person: any = [];
  objectProps=[];
  issuerConfigForm:FormGroup;

  constructor(

  ) {
    this.person = {
      lcid: {
        label: 'LCID:',
        value: '2',
        type: 'text'
      }
    };
    this.generateView();
  }
  //Assume the values coming from modal window
  addbtn = function () {
    Object.assign(this.person, {
      ipa: {
        label: 'IP:',
        value: '1',
        type: 'text'
      },
    });

    this.generateView();

  };

  generateView(){
      // remap the API to be suitable for iterating over it
      this.objectProps =
      Object.keys(this.person)
          .map(prop => {
              return Object.assign({}, { key: prop }, this.person[prop]);
          });

  // setup the form
  const formGroup = {};
  for (let prop of Object.keys(this.person)) {
      formGroup[prop] = new FormControl(this.person[prop].value || '');
  }
   this.issuerConfigForm = new FormGroup(formGroup);


  }
}

dynamicform.component.ts

@Component({
  selector: 'dynamic-form',
    template: `
    <form novalidate (ngSubmit)="onSubmit(form.value)" [formGroup]="formGroup" class="form-horizontal">
      <div *ngFor="let prop of objectProps">
      <div class="form-group row">
        <label [attr.for]="prop" class="col-sm-2 control-label">{{prop.label}}</label>

        <div [ngSwitch]="prop.type" class="col-sm-4">
                <input *ngSwitchCase="'text'" 
                    [formControlName]="prop.key"
                    [id]="prop.key" [type]="prop.type" class="form-control">
        </div>
          </div>
      </div> 
            <button type="submit">Submit</button>
    </form>
  `
})
export class DynamicFormComponent implements OnInit {
 @Input() formGroup;
 @Input() objectProps;
  constructor() { }

  ngOnInit() {
  }

}

Upvotes: 0

Dinh Duong
Dinh Duong

Reputation: 297

You can follow my below example to render dynamic form:

export class AppComponent implements OnInit {
  fields = {
    name: {
      type: 'text',
      value: 12,
      label: 'Name'
    }
  }

  fieldProps = Object.keys(this.fields);
  form: FormGroup;
  formControls = {};
  constructor(private fb: FormBuilder) {

    this.fieldProps.forEach(prop => {
      this.formControls[prop] = [this.fields[prop].value];
    })
    this.form = this.fb.group(this.formControls);
    this.form.valueChanges.subscribe(v => console.log(v));
  }
  ngOnInit(): void {
  }
}
<form [formGroup]="form">
  <div *ngFor="let prop of fieldProps">
    <label>{{fields[prop].label}}</label>
    <div [ngSwitch]="fields[prop].type">
      <input *ngSwitchCase="'text'" [formControlName]="prop">
    </div>
  </div>
</form>

enter image description here


Finally, you should use @Output to emit the form.value to the <issuer component>. I think we should define the controls structure data more clearly to easy to understand and rendering.

Upvotes: 1

Related Questions