Reputation: 343
I have just started working on the Angular reactive forms and i was trying to build a table which is inside a form.
The table has add new feature by clicking it new empty row will be inserted in the table. the existing rows will be in edit mode by default and they are validated. The table data will be saved with a single save button which is out of the table but inside the form. I tried the below code
constructor(private router: Router, private fb: FormBuilder) { }
columnsToDisplay: string[];
dataList;
copyDataList;
rows: FormArray = this.fb.array([]);
formGroup: FormGroup = this.fb.group({ actualsVolumeData: this.rows });
ngOnInit() {
this.columnsToDisplay = ['id', 'code', 'desc'];
this.formGroup = this.fb.group({
columns: this.columnsToDisplay,
});
this.copyDataList = [];
this.dataList = [];
let list = [
{
code: 'one',
desc: 'One1',
id: 1
},
{
code: 'two',
desc: 'Two1',
id: 2
},
{
code: 'three',
desc: 'Three1',
id: 3
},
];
this.copyDataList = new MatTableDataSource(list);
this.dataList = new MatTableDataSource(list);
}
onAdd() {
let newRow = {
id: this.dataList.data.length + 1,
code: undefined,
desc: undefined
}
this.copyDataList.data.push(newRow);
this.dataList = new MatTableDataSource(this.copyDataList.data);
}
onSubmit() {
}
<form [formGroup]=`formGroup`>
<button mat-button (click)=`onAdd()`>Add</button>
<table mat-table [dataSource]=`dataList` [formArrayName]=`actualsVolumeData` class=`mat-elevation-z8`>
<ng-container matColumnDef=`id`>
<th mat-header-cell *matHeaderCellDef> ID </th>
<td mat-cell *matCellDef=`let element, let i = index` [formGroupName]=`i`> {{element.id}} </td>
</ng-container>
<ng-container matColumnDef=`code`>
<th mat-header-cell *matHeaderCellDef> Code </th>
<td mat-cell *matCellDef=`let element, let i = index` [formGroupName]=`i`> {{element.code}}
<mat-form-field>
<input matInput formControlName='code'>
</mat-form-field>
</td>
</ng-container>
<ng-container matColumnDef=`desc`>
<th mat-header-cell *matHeaderCellDef> Description </th>
<td mat-cell *matCellDef=`let element, let i = index` [formGroupName]=`i`> {{element.desc}}
<mat-form-field>
<input matInput formControlName=`desc`>
</mat-form-field>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef=`columnsToDisplay`></tr>
<tr mat-row *matRowDef=`let row; columns: columnsToDisplay;`></tr>
</table>
<button mat-button (click)=`formGroup.valid && onSubmit()`>submit</button>
</form>
but am getting this error this.validator is not a function
Upvotes: 1
Views: 5720
Reputation: 57929
How make a formArray from data
Imagine you has a data like
[
{code: 'one',desc: 'One1',id: 1},
{code: 'two',desc: 'Two1',id: 2},
{code: 'three',desc: 'Three1',id: 3}
]
To create a formArray it's useful has a function that, received an object and return a FormGroup
createFormGroup(data):FormGroup
{
data=data|| {code:'',desc:'',id:0}
return new FormGroup({
code:new FormControl(data.code,Validators.required),
desc:new FormControl(data.desc,Validators.required),
id:new FormControl(data.id,Validators.required)
})
}
if we call to the function with an object return a formGroup, if we call the function with null, return also a FormGroup with the elements empty
When you has the data you can do simple
this.myFormArray=new FormArray(this.data.map(x=>this.createFormGroup(x)))
That's each element of data convert to a formGroup, the formArray will be an array with this elements. map transform each element "x" in "this.createFormGroup(x)"
If you has a service that return the data you subscribe
this.myService.getData().subscribe(res=>{
this.myFormArray=new FormArray(res.map(x=>x.this.createFormGroup(x)))
})
//your service has a method like
getData()
{
return this.httpClient("http:myUrl")
}
The good of this aproach is that to add a new element to the FormArray you only need make
this.formArray.push(this.createFormGroup(null))
To remove
this.formArray.removeAt(index)
In stackblitz has a little example of all this. Well, in service I use the rxjs operator of
normally was a httpClient.get(url). Only I put the part of create the formArray,
NOTE: I use the contructor of FormGroup and the constructor of FormArray, but you can use BuilderForm like
createFormGroup(data):FormGroup
{
data=data|| {code:'',desc:'',id:0}
return this.fb.group({
code:[data.code,Validators.required],
desc:[data.desc,Validators.required],
id:[data.id,Validators.required]
})
}
And use
myFormArray=this.fb.array(res.map(x=>x.this.createFormGroup(x)))
Upvotes: 1