问题
I am building a form group with an array inside of it, using mat-table
data source.
I started by creating the table:
<form *ngIf="formGroup" [formGroup]="formGroup">
<table mat-table [dataSource]="dataSource" *ngIf="total>0" formArrayName="distribution">
<ng-container matColumnDef="ind_id">
<th mat-header-cell *matHeaderCellDef> ID </th>
<td mat-cell *matCellDef="let element">{{element.individual_id}}</td>
</ng-container>
<ng-container matColumnDef="ind_name">
<th mat-header-cell *matHeaderCellDef> Name </th>
<td mat-cell *matCellDef="let element">{{element.ind_name}}</td>
</ng-container>
<ng-container matColumnDef="ind_status">
<th mat-header-cell *matHeaderCellDef> Ind. Status </th>
<td mat-cell *matCellDef="let element">{{element.ind_status}}</td>
</ng-container>
<ng-container matColumnDef="project_kit">
<th mat-header-cell *matHeaderCellDef> Kits </th>
<td mat-cell *matCellDef="let element; let i=index;">
<div [formGroupName]="i">
<mat-form-field color="warn" appearance="fill">
<mat-label>Kits</mat-label>
<mat-select formControlName="kit" id="kit" placeholder="Kit">
<mat-option *ngFor="let pk of projectKit" [value]="pk.project_kit">{{pk.kit_name}} part of project
{{pk.project_name}}</mat-option>
</mat-select>
</mat-form-field>
</div>
</td>
</ng-container>
<ng-container matColumnDef="actual_date">
<th mat-header-cell *matHeaderCellDef> Actual Date </th>
<td mat-cell *matCellDef="let element; let i=index;">
<div [formGroupName]="i">
<mat-form-field color="warn" appearance="legacy">
<mat-label>Actual Dist. Date</mat-label>
<input formControlName="actual_date" matInput [matDatepicker]="picker2" placeholder="Actual Date">
<mat-datepicker-toggle matSuffix [for]="picker2"></mat-datepicker-toggle>
<mat-datepicker #picker2></mat-datepicker>
</mat-form-field>
</div>
</td>
</ng-container>
<ng-container matColumnDef="note">
<th mat-header-cell *matHeaderCellDef> Note </th>
<td mat-cell *matCellDef="let element;let i=index;">
<div [formGroupName]="i">
<mat-form-field color="warn" appearance="legacy">
<mat-label>Note</mat-label>
<input formControlName="note" matInput type="text" placeholder="Note">
</mat-form-field>
</div>
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let element">
<button mat-raised-button color="warn" type="submit" color="warn" (click)="addDist(element)">
<mat-icon>add</mat-icon> Add
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"
}"></tr>
</table>
</form>
And for the typescript:
this.formGroup = this.fb.group({
distribution: this.fb.array([
this.createArray()
])
});
And createArray():
createArray(): FormGroup {
return this.fb.group({
'note': new FormControl(''),
'kit': new FormControl(''),
'actual_date': new FormControl('')
});
The first typescript is fired when the user upload a file:
<form [formGroup]="uploadForm" role="form">
<input #fileInput type="file" formControlName="upload" value="Upload Excel/CSV file"
(change)="upload($event.target.files)" accept=".xlsx, .xls, .csv" />
<button mat-raised-button id="inputFile" color="accent" (click)="reset()">
<mat-icon color="warn">cancel</mat-icon>Reset
</button>
</form>
There is no error for the previous form of uploading a file.
But when I upload a file an error appears for the form array:
Error: Cannot find control with path: 'distribution -> 1' at _throwError (forms.js:1790) at setUpFormContainer (forms.js:1772) at FormGroupDirective.push
And it's pointing on:
<div [formGroupName]="i">
<mat-form-field color="warn" appearance="fill">
<mat-label>Kits</mat-label>
<mat-select formControlName="kit" id="kit" placeholder="Kit">
<mat-option *ngFor="let pk of projectKit" [value]="pk.project_kit">{{pk.kit_name}} part of project
{{pk.project_name}}</mat-option>
</mat-select>
</mat-form-field>
</div>
EDIT
After Adding {{formGroup.value| JSON}}
I got this:
{ "distribution": [ { "note": "", "kit": "", "actual_date": "" } ] }
回答1:
the problem is your dataSource, let i=index make reference to the values of the dataSource, if you has less element in your array than in your dataSource your code crash
If all your elements in the table belongs to the FormArray it's "easy", you can see an example in this stackblitz
There are two keys,
one how create the Form
myform:FormGroup=new FormGroup({
distribution:new FormArray(ELEMENT_DATA.map(x=>{
return new FormGroup({
position:new FormControl(x.position),
name:new FormControl(x.name),
weight:new FormControl(x.weight),
symbol:new FormControl(x.symbol),
})}))
});
And, how We refereed to the controls
<form *ngIf="myform" [formGroup]="myform">
<ng-container formArrayName="distribution">
<!--see that datasource is myForm.get('distribution').controls-->
<table mat-table [dataSource]="myform.get('distribution').controls" class="mat-elevation-z8" >
<ng-container matColumnDef="position">
<th mat-header-cell *matHeaderCellDef> No. </th>
<!--so, "element" is a formGroup-->
<td mat-cell *matCellDef="let element;let i=index" [formGroup]="element"> <input formControlName="position" > </td>
</ng-container>
....
</table>
</ng-container>
</form>
But you has a "dataSource" and a formArray some disconected. You can create a function to referred to the array
get distributionArray()
{
return this.myForm.get('distribution') as FormArray
}
And use in your td some like
<td mat-cell *matCellDef="let element;let i=index"
[formGroup]="distributionArray.at(i)">
<input formControlName="name" >
</td>
}
well, it's not necesary has value to all, but must there are so many elements in the array as in the dataSource, e.g.
this.myform:FormGroup=new FormGroup({
distribution:new FormArray(ELEMENT_DATA.map(()=>{
//only two properties empty
return new FormGroup({
weight:new FormControl(),
symbol:new FormControl(),
})}))
});
Or using push
this.myform:FormGroup=new FormGroup({
distribution:new FormArray([])
});
ELEMENT_DATA.forEach(()=>{
(this.myForm.get('distribution') as FormArray).push(this.createArray())
}
回答2:
Ok. so it means you're not adding enough formgroups in array. You are calling create only once. so only first one in array works. when the index goes to 1 from 0 there is no formgroup in formarray. As I understand you want to create formgroup for each row. so you need to call your function many times.
this.formGroup = this.fb.group({
distribution: this.fb.array([
this.createArray()
])
});
instead of creating array every time you should probably push new formgroup to existing formarray.
write a getter for distribution for easier use.
get distribution() {
this.formGroup.get('distribution') as FormArray;
}
then you can do
this.distribution.push(this.fb.group({
'note': new FormControl(''),
'kit': new FormControl(''),
'actual_date': new FormControl('')
}));
for each row. I'm not sure where you are doing create but this would go there for each row.
and keep the formGroup to i instead of i-1 or 0.
回答3:
You have to define form array name above your [formGroup]
start like this formArrayName="distribution"
来源:https://stackoverflow.com/questions/55240595/angular-7-and-form-arrays-error-of-cannot-find-a-control-with-path