I created a form using custom inputs and ControlValueAccessor
which you can see here. After finally getting it to work I noticed whenever I delete an item from a FormArray
it clears the values of the other FormGroups
still present in the array. If you run the demo and click on the add-media-query
button a couple times and fill one out then delete one of the others you'll see what I mean. The code bellow is an example of how I have everything set up.
The form
SvgForm : FormGroup<SvgForm> = new FormGroup<SvgForm>({
title: new FormControl<string>(''),
graphicId: new FormControl<string>(''),
svgInput : new FormControl<string>(''),
viewBox : new FormGroup<ViewBoxParams>({
x: new FormControl<string>(''),
y: new FormControl<string>(''),
width: new FormControl<string>(''),
height: new FormControl<string>('')
}),
styling: new FormGroup<StylingParams>({
globalStyles: new FormControl<string>(''),
mediaQueries: new FormArray<FormGroup<MediaQueryParams>>([])
})
});
Inside the template I pass the styling
FormGroup into a custom component called styling-input
through an @Input()
like so
<styling-input [StylingForm]="SvgForm.controls.styling"></styling-input>
This is how the styling-input
is set up.
styling-input.component.ts
@Component({
selector: 'styling-input',
templateUrl: './styling-input.component.html',
styleUrls: ['./styling-input.component.css'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: StylingInputComponent,
multi: true
}
]
})
export class StylingInputComponent implements ControlValueAccessor{
@Input() StylingForm!: FormGroup<StylingParams>;
get MediaQueryList() {
return this.StylingForm.get('mediaQueries') as FormArray<FormGroup<MediaQueryParams>>;
}
writeValue(value: any){ if(value){ this.StylingForm.setValue(value); } }
registerOnChange(fn: any){ this.StylingForm.valueChanges.subscribe(fn); }
registerOnTouched(onTouched: Function){}
private createMediaQueryGroup(): FormGroup<MediaQueryParams> {
return new FormGroup<MediaQueryParams>({
selectorParams: new FormGroup<MediaSelectorParams>({
mediaType: new FormControl<MediaTypeParams>('all'),
expressions: new FormArray<FormGroup<MediaExpressionGroupParams>>([]),
}),
rules: new FormControl<string>('')
});
}
public addMediaQuery():void{
this.MediaQueryList.push(this.createMediaQueryGroup());
}
public removeMediaQuery(item: number): void{
this.MediaQueryList.removeAt(item);
}
}
Then inside the template I iterate over the MediaQueryList
getter like this
<article formArrayName="mediaQueries">
<media-query-input *ngFor="let a of MediaQueryList.controls; let i = index"
[MediaQueryForm]="a"
[attr.GroupId]="i"
(RemoveGroup)="removeMediaQuery($any($event))"
></media-query-input>
</article>
The property MediaQueryForm
is an @Input()
that I pass the FormGroup
into and the i
variable is passed back up through the RemoveGroup
Output once a delete
button is pushed and you can see in the code above the removeMediaQuery()
function uses removeAt()
on the MediaQueryList
.
I haven't really tried anything different as this is the suggested way to remove an element from a FormArray
, however due to me using ControlValueAccessor
I'm guessing there may be some other things going on under the hood that I'm not aware of. Does anybody know why this happens and how to fix it?
CodePudding user response:
You are not passing GroupId
input value to the media-query-input component.
Instead of setting attribute value pass input using without attr prefix.
[attr.GroupId]="i" ===> [GroupId]="i"
styling.input.component
<media-query-input *ngFor="let a of MediaQueryList.controls; let i = index"
[MediaQueryForm]="a"
[GroupId]="i"
(RemoveGroup)="removeMediaQuery($any($event))" >
</media-query-input>