tangel
tangel

Reputation: 393

How can we get form value in Angular as object with multiple properties?

I have an array of objects as:

arr = [{
    "name": "firstName",
    "type": "text",
    "required": false
  },
  {
    "name": "lastName",
    "type": "text",
    "required": false
  },
    {
    "name": "dob",
    "type": "date",
    "required": false
  }
]

I am creating a form in HTML using *ngFor and formControlName as follows:

createFormgroup() {
  arr.forEach((control: any) => {
    this.createFormGroup.addControl(
      control.name,
      this.fb.control(false)
    );
    this.createFormGroup.addControl(
      "required" + i,
      this.fb.control(false)
    );
  });
}

submit(form: NgForm) {
  console.log(form.value)
}
<mat-label>Enter form name:</mat-label>
<input class="form-name-input" matInput placeholder="Form Name" formControlName="formName" required>

<form [formGroup]="createFormGroup" (ngSubmit)="submit(createFormGroup)">
  <div *ngFor="let a of arr; let i = index">
    <mat-checkbox formControlName="{{a.name}}">{{ a.name }}</mat-checkbox>
    <mat-checkbox formControlName="required{{i}}">required</mat-checkbox>
  </div>
  <button type="submit">submit</button>
</form>

Here I am trying to achieve to show and post the values together as an object respective to the other. For example, if I check the First name, dob, and the required in HTML, I want the whole object when clicked on submit(). When clicked on submit(), I am looking for output like:

form.value = [{
  "name": "firstname",
  "type": "text",
  required: true
}, {
  "name": "dob",
  "type": "date",
  required: false
}]

I am confused about how to achieve this. Is there a way? Also, I need to assign different formControl names for each required field. I am not sure how to do that.

I am attaching an image for understanding. UI image

Note: I know how to get values from a form, but I am looking for a way to copy the whole object

To keep it simple: I need same array which I used to create form, and at the end I need same array with same object properties changed as per user input

Upvotes: 4

Views: 6875

Answers (2)

navnath
navnath

Reputation: 3714

1st Change :

this.createFormGroup.addControl(
  "required" + i,
  this.fb.control(false)
);

to

this.createFormGroup.addControl(
  "required_" + control.name,
  this.fb.control(false)
);

// your current result
 const obj =  {
 'firstName': true,
 'required_firstName': true,
 'lastName': true,
 'required_lastName': false,
 'dob': true,
 'required_dob': true
};
 
 // your array
const arr = [
    {
        name: "firstName",
        type: "text",
        required: false
    },
    {
        name: "lastName",
        type: "text",
        required: false
    },
    {
        name: "dob",
        type: "date",
        required: false
    }
];

const validKeys = Object.keys(obj).filter(key => obj[key] && (key.split("_").length==1));

// your required output
const finalResult = arr.filter(item => validKeys.includes(item.name)).map(item => ({...item, required: obj['required_'+item.name]}));

console.log(finalResult);

Upvotes: 1

Amer
Amer

Reputation: 6706

You can achieve that like the following:

  • Define a sub-form group for each item in the source array, and add it to the main formGroup.
  • Add change event handler for the name checkboxes, to disable/enable the sub-form group, to be excluded from the value (In Angular, if the form-control or sub-form-group is disabled then its value will be excluded from the FormGroup.value)
  • Loop through the form array in the component template, and bind to the required form-control only.

So the component class, will look something like the following:

ngOnInit() {
  this.formGroup = this.fb.group({
    formName: null,
    arrForm: this.fb.array(this.createSubFormGroup())
  });
}

createSubFormGroup() {
  return this.arr.map(
    (control: { name: string; type: string; required: boolean }) => {
      const subForm = this.fb.group({
        name: control.name,
        type: control.type,
        required: control.required
      });

      // disable by default to be not included with the value until it's checked.
      subForm.disable();

      return subForm;
    }
  );
}

/** To enable/disable the sub formGroup if the checkbox checked/unchecked */
onNameCheckChange(ndx: number, event: MatCheckboxChange) {
  (this.formGroup.get('arrForm') as FormArray).controls[ndx][event.checked ? 'enable' : 'disable']();
}

submit() {
  console.log(this.formGroup.value?.arrForm || []);
}

And the component template, will look something like the following:

<form [formGroup]="formGroup" (ngSubmit)="submit()">
  <mat-label>Enter form name:</mat-label>
  <input class="form-name-input" matInput placeholder="Form Name" formControlName="formName" required>

  <div formArrayName="arrForm">
    <div *ngFor="let a of arr; let i = index" [formGroupName]="i">
      <mat-checkbox (change)="onNameCheckChange(i, $event)">{{ a.name }}</mat-checkbox>
      <mat-checkbox formControlName="required">required</mat-checkbox>
    </div>
  </div>
  <button type="submit">submit</button>
</form>

And here is the working StackBiltz

Upvotes: 2

Related Questions