angularcheckboxmatformarrayangular-material-table

Dynamical adding checkbox to each row in table angular


enter image description here

In the above image i want to add checkbox and its label dynamical to each row when click on 'Add New checkbox' button.

so when i click on it button will visible a input box and type the checkbox name and submit the new checkbox will visible only in that particular row.

i think we can achieve through the form array but not getting the result.

example: adding a new checkbox in the ENMT row so i click add new button and input box visible and add a label 'Other' so a new checkbox called 'other' will appear on the row

enter image description herei am

expecting like this a new field on ENMT


Solution

  • I spent a significant amount of time to figure it out for you.

    First of all let's clean up your data model. Let's call each line a LineItem and each checkbox a Characteristic. I created for this appr. interfaces and created an array with sample data:

    interface Characteristic {
      name: string;
      value: boolean;
    }
    
    export interface LineItem {
      label: string;
      characteristics: Characteristic[];
    }
    

    After that let's define the form:

    form = this.fb.group({
        lineItems: this.fb.array([
          this.fb.group({
            label: '',
            characteristics: this.fb.array([
              this.fb.group({ name: '', value: false }),
            ]),
          }),
        ]),
      });
    

    In this example I consider the whole table as a form and not the single rows.

    The rows are the lineItems, which is a FormArray and it holds a FormGroup with controls like label and characteristics (which is a FormArray too, with the controls name and value).

    Then I define a getter for easier access in the template:

    get lineItems() {
        return this.form.get('lineItems') as FormArray;
      }
    

    In the ngOnInit I fill up the form with the sample data:

    ngOnInit() {
        const lineItems = <FormArray>this.form.get('lineItems');
        lineItems.clear();
    
        this.data.forEach((lineItem) => {
          let newLineItem = this.fb.group({
            label: '',
            characteristics: this.fb.array([]),
          });
          lineItems.push(newLineItem);
          lineItem.characteristics.forEach((char) => {
            let newChar = this.fb.group({ name: '', value: false });
            (<FormArray>newLineItem.get('characteristics')).push(newChar);
          });
        });
    
        this.form.setValue({ lineItems: this.data });
        console.log('initial form value: ', this.form.value);
      }
    

    If we click on the appr. 'add new checkbox' button, I add the new checkbox with the name 'other' to the corresponding row:

    onAddNewCheckbox(row: number) {
        const newChar = this.fb.group({ name: 'other', value: false });
        (<FormArray>this.lineItems.controls[row].get('characteristics')).push(
          newChar
        );
      }
    

    Btw, you could use here some input dialog in order to get the name from the user.

    After that we have to set up the template:

    <form [formGroup]="form" (ngSubmit)="onSubmit()">
      <div formArrayName="lineItems">
        <table>
          <tr *ngFor="let lineItem of lineItems.controls; let row = index">
            <div style="display: flex" [formGroupName]="row">
              <td>{{ lineItem.get('label').value }}</td>
              <div style="display: flex" formArrayName="characteristics">
                <td
                  *ngFor="
                    let char of lineItem.get('characteristics').value;
                    let column = index
                  "
                >
                  <div [formGroupName]="column">
                    <input
                      type="checkbox"
                      id="{{ row }} - {{ column }}"
                      formControlName="value"
                    />
                    <label for="{{ row }} - {{ column }}">{{ char.name }}</label>
                  </div>
                </td>
                <button (click)="onAddNewCheckbox(row)" type="button">
                  add new checkbox
                </button>
              </div>
            </div>
          </tr>
        </table>
      </div>
      <button type="submit">submit</button>
    </form>
    

    I used a simple table element for this, but the logic should be similar for mat-table.

    If you then click the checkboxes or add new ones and hit the submit button, you get a form value like this:

    form value

    Here you can find a working stackblitz example.