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
expecting like this a new field on ENMT
CodePudding user response:
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:
Here you can find a working stackblitz example.