In my parent component, I have a FormGroup
with a FormArray
, and I want to handle that array in a child component. The parent's HTML does this:
<ng-container [formGroup]="formGroup">
<app-child formArrayName="theArrayName">
I assumed in the child I would inject the NgControl
and then have access:
@Component({
...,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ChildComponent),
multi: true
}
]
})
export class ChildComponent implements ControlValueAccessor {
constructor(private readonly control: NgControl) {
this.formArray = control.control as FormArray<...>
}
I always get a null injector saying there's no provider for NgControl
.
Complementary the Chris Hamilton's answer
<app-child [formArray]="form.get('formArrayNme')"></app-child>
The problem when you mannage a formArray in a child that you pass as input is that you can not use the "typical" constructor of manage FormArrays. You should define a function (*)
//if is a FormArray of FormControls
getControl(index:number)
{
return this.formArray.at(index) as FormControl
}
//if is a FormArray of FormGroup
getGroup(index:number)
{
return this.formArray.at(index) as FormGroup
}
And use
<!--if is a formArray of FormControls-->
<div *ngFor="let control of FormArray;let i=index">
<input [formControl]=getControl(i)>
</div>
<!--if is a formArray of FormGroups-->
<div *ngFor="let group of FormArray;let i=index" [formGroup]="getGroup(i)>
<input formControlName="prop1">
<input formControlName="prop2">
</div>
If we want to use the typical FormArray with formArrayName we need viewProvider the FormGroupDirective and know the name. We can do using a child-control like
@Component({
selector: 'child-array',
templateUrl: 'child-array.html',
viewProviders:[
{ provide: ControlContainer, useExisting: FormGroupDirective }]
})
export class ChildComponent {
array:FormArray
arrayName:string="fool"
@Input() name: string;
@Input('array') set _(value)
{
this.array=value as FormArray
this.arrayName=Object.keys(this.array.parent.controls)
.find(key=>this.array.parent.get(key)==this.array)
}
}
Now we can use
<!--if is a formArray of FormControls-->
<div [formArrayName]="arrayName">
<div *ngFor="let control of array.controls;let i=index">
<input [formControlName]="i">
</div>
</div>
<!--if is a formArray of FormGroups-->
<div [formArrayName]="arrayName">
<div *ngFor="let control of array.controls;let i=index" [formGroupName]="i">
<input formControlName="prop1">
<input formControlName="prop2">
</div>
</div>
This second approach (in the case of formArray of FormControls) can be see in this stackblitz
(*)I know that some authors use the variable of the loop to get the value of the formControl or the FormGroup
<div *ngFor="let group of formArray.controls" [formGroup]="group">
Unfortunaly, this don't work since Angular 12, because group is only an "AbstractControl". If you has strict mode you received an error saying you that an AbstractControl is not a FormGroup (works in early Angular versions).
Some suggest use the $any, but (personal opinion) is a "ugly work-around" or even de-activate the strict mode (it's a very very very bad idea and this last is not a personal opinion)