Problem
Given the following form:
<form [formGroup]="additional_providers_form">
<div formArrayName="providers">
@for(provider of providers.controls; track provider; let i = $index) {
<div class="row" [formGroupName]="i">
<div class="col mb-3">
<app-form-control
type="text"
label="Provider Name"
formControlName="provider_name"
[required]="true"
></app-form-control>
</div>
<div class="col mb-3">
<app-form-control
type="text"
label="Provider License"
formControlName="provider_license"
[required]="true"
></app-form-control>
</div>
</div>
}
</div>
<div class="col text-center">
<div class="d-grid gap-2">
<button type="submit" class="btn btn-black btn-lg btn-primary rounded-0 mt-5">
Save and Complete
</button>
</div>
</div>
</form>
I am trying to dynamically create an array list of components for a user to add or remove information for an application. At this point I am working on initializing the form components.
You will see my attempt to initialize the form in the following template, on line 19 and upon initialization create the first element (FormGroup) in the form array.
import { Component } from '@angular/core';
import { FormArray, FormBuilder, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-additional-providers',
standalone: true,
imports: [ReactiveFormsModule],
templateUrl: './additional-providersponent.html',
styleUrl: './additional-providersponent.scss'
})
export class AdditionalProvidersComponent {
additional_providers_form!: FormGroup
constructor(private fb: FormBuilder) {
}
ngOnInit() {
this.additional_providers_form = this.fb.group({
providers: this.fb.array([this.createProvider()])
})
}
get providers(): FormArray {
return this.additional_providers_form.get('providers') as FormArray
}
addProvider() {
this.providers.push(this.createProvider())
}
removeItem(idx: number) {
this.providers.removeAt(idx)
}
private createProvider() {
return this.fb.group({
provider_name: [''],
provider_license: ['']
})
}
}
The formControl
objects for the above component are located within the custom component app-form-control
given here:
import { CommonModule } from '@angular/common';
import { Component, forwardRef, Input, OnChanges, OnInit, Optional, Self, SimpleChanges } from '@angular/core';
import { ControlContainer, FormControl, FormGroupDirective, FormsModule, NG_VALIDATORS, NG_VALUE_ACCESSOR, NgControl, ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-form-control',
standalone: true,
imports: [ReactiveFormsModule],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => FormControlComponent),
multi: true
}
],
templateUrl: './form-controlponent.html',
styleUrl: './form-controlponent.scss'
})
export class FormControlComponent implements OnInit, OnChanges {
@Input() type: string = 'text'
@Input() label: string = ''
@Input() placeholder: string = ''
@Input() isValid: boolean = false
@Input() required: boolean = false
value: any
disabled: boolean = false;
private touched = false
dirty = false
onTouched: any = () => {}
constructor(@Optional() @Self() public ngControl: NgControl) {
if (this.ngControl != null) {
this.ngControl.valueAccessor = this
}
}
ngOnInit() {
}
onChange: any = (value: boolean) => {}
ngOnChanges(changes: SimpleChanges): void {
}
registerOnTouched(fn: any) {
this.onTouched = fn
}
registerOnChange(fn: any) {
this.onChange = fn;
}
valueChanged(value: any) {
this.onChange(value)
this.markAsTouched()
this.markAsDirty()
}
writeValue(value: any) {
this.value = value
}
setDisabledState(isDisabled: boolean) {
this.disabled = isDisabled
}
private markAsTouched(): void {
if(!this.touched) {
this.touched = true
this.onTouched()
}
}
private markAsDirty(): void {
if(!this.dirty) {
this.dirty = true
}
}
}
with the html template here:
<label
class="form-label required"
[class.required]="required">{{label}}</label>
<input
#val
[type]="type"
class="form-control"
[formControl]="ngControl?.control"
[class.is-invalid]="dirty && !isValid"
[class.is-isvalid]="dirty && isValid"
(change)="valueChanged(val.value)"/>
When trying to initialize the form in this capacity I am receiving the following error:
main.ts:7 ERROR RuntimeError: NG01203: No value accessor for form control path: 'providers -> 0 -> provider_license'.
I know what the error means and why it is happening, however I do not know how to fix it.
Knowns
The error is being cause by the custom form element because it doesn't recognize that it is a control component
The error is being thrown because the Component is being created dynamically in a for loop
Attempted Solutions
The first potential solution I found was based on an answer to the following question. You will see in the app-form-control
template where I added [formControl]="ngControl?.control"
and in the typescript I added:
constructor(@Optional() @Self() public ngControl: NgControl) {
if (this.ngControl != null) {
this.ngControl.valueAccessor = this
}
}
The idea was to use the standalone approach and give it access to the ValueAccessor without worrying about the parent wrapper, but that failed.
Another solution was putting the inputs in as normal without using the custom component, and it doesn't throw the error. I built the custom component to reuse, and would like to avoid the solution not using it.
I have yet to find additional solutions that would help solve my problem.
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745354486a4624033.html
评论列表(0条)