When you use "custom Form Control", you need take account that you feed the cursom Form Control with a Form Control (not FormArray, not FormGroup). The FormControl has as value an array or an object, but you need not confussed about this.(*)
You can see in work in stackblitz
That's your form is like
//in main.form
this.requestForm = new FormGroup({
garageId: new FormControl(0),
routes: new FormControl(routes), //<--routes will be an array of object
endDateTime: new FormControl(0)
})
//in cva-form-array
this.form=new FormArray([new FormControl(...)]); //<-this.form is a
//formArray of FormControls NOT of formGroup
//finally in your cva-form
this.form=new FormGroup({});
this.form=formGroup({
addressPointId: new FormControl(),
municipalityId: new FormControl(),
...
})
I've create a const to export to simply the code. MY const expor is
export const dataI = {
addressPointId: "",
municipalityId: "",
regionId: "",
rvId: "",
sequenceNumber: "",
settlementId: "",
regionName: "",
municipalityName: "",
settlementName: "",
description: "",
}
So, in mainForm we have
ngOnInit() {
let routes:any[]=[];
routes.push({...dataI});
this.requestForm = new FormGroup({
garageId: new FormControl(0),
routes: new FormControl(routes),
endDateTime: new FormControl(0)
})
}
<mat-card [formGroup]="requestForm" style="background: #8E8D8A">
<app-cva-form-array formControlName="routes"></app-cva-form-array>
</mat-card>
In cvc-form array create the formArray when we give value
writeValue(v: any) {
this.form=new FormArray([]);
for (let value of v)
this.form.push(new FormControl(value))
this.form.valueChanges.subscribe(res=>
{
if (this.onChange)
this.onChange(this.form.value)
})
}
<form [formGroup]="form" >
<mat-card *ngFor="let route of form.controls;
let routeIndex = index; let routeLast = last;">
<button (click)="deleteRoute(routeIndex)">
cancel
</button>
<app-cva-form [formControl]="route" (blur)="onTouched()"></app-cva-form>
</form>
Finally, the cva-form
writeValue(v: any) {
this.form=new FormGroup({});
Object.keys(dataI).forEach(x=>{
this.form.addControl(x,new FormControl())
})
this.form.setValue(v, { emitEvent: false });
this.form.valueChanges.subscribe(res=>{
if (this.onChanged)
this.onChanged(this.form.value)
})
}
<div [formGroup]="form">
<mat-form-field class="locationDate">
<input formControlName="regionName">
<mat-autocomplete #region="matAutocomplete"
(optionSelected)="selectedLocation($event)">
<mat-option *ngFor="let region of regions"
[value]="region">
{{region.regionName}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
<mat-form-field class="locationDate">
<input formControlName="municipalityName"
[matAutocomplete]="municipality"
(blur)="onTouched()"
[readonly]="checked || this.form.value.regionId < 1">
....
</form>
(*) Yes, we are used to seeing that a FormControl has as a value a string or a number, but no one forbids us that the value is an object or an array (for example, ng-bootstrap DatePicker stores an object {year: .. month: .., day ..}, mat-multiselect stores an array, ...)
Update Of course we can feed our control with data from a service or similar. The only thing we must take account is how we give the data. As usually I like make a function that received a data or null and return a FormControl
getForm(data: any): FormGroup {
data = data || {} as IData;
return new FormGroup({
garageId: new FormControl(data.garageId),
routes: new FormControl(data.routes),
endDateTime: new FormControl(data.endDateTime)
})
}
where IData is an interface
export interface IData {
garageId: number;
routes: IDetail[];
endDateTime: any
}
and IDetail another interface
export interface IDetail {
addressPointId: string;
...
description: string;
}
Then we can have a complex data like (sorry for the large object)
let data = {
garageId: 1,
routes: [{
addressPointId: "adress",
municipalityId: "municipallyty",
regionId: "regionId",
rvId: "rvId",
sequenceNumber: "sequenceNumber",
settlementId: "settlementId",
regionName: "regionName",
municipalityName: "municipalityName",
settlementName: "settlementName",
description: "description",
},
{
addressPointId: "another adress",
municipalityId: "another municipallyty",
regionId: "another regionId",
rvId: "another rvId",
sequenceNumber: "another sequenceNumber",
settlementId: "another settlementId",
regionName: "another regionName",
municipalityName: "another municipalityName",
settlementName: "another settlementName",
description: "another description",
}],
endDateTime: new Date()
}
Then only need make
this.requestForm = this.getForm(data);
The stackblitz if updated