This is a repost of the deleted question from JOYBOY
with title NG01203: No value accessor for form control name: 'name'
I've created Angular 18 application. I'm using both combination of standalone components and module wise components.
For re-usability, created input component. But I'm getting error
ERROR Error: NG01203: No value accessor for form control name: 'name'. Find more at https://angular.dev/errors/NG01203
at _throwMissingValueAccessorError (forms.mjs:3420:9)
at setUpControl (forms.mjs:3202:29)
at _FormGroupDirective.addControl (forms.mjs:5320:5)
at _FormControlName._setUpControl (forms.mjs:6011:39)
at _FormControlName.ngOnChanges (forms.mjs:5960:28)
at _FormControlName.rememberChangeHistoryAndInvokeOnChangesHook (core.mjs:3975:10)
at callHookInternal (core.mjs:5004:10)
at callHook (core.mjs:5031:5)
at callHooks (core.mjs:4988:9)
at executeInitAndCheckHooks (core.mjs:4943:5)
type InputProps = {
formGroup: FormGroup;
type: 'text' | 'password' | 'number';
value: string;
label: string;
formControlName: string;
placeholder: string;
checkValidation: boolean;
}
@Component({
selector: 'app-input',
standalone: true,
imports: [
FormsModule,
ReactiveFormsModule,
NgClass
],
templateUrl: './input.component.html',
})
export class InputComponent {
public readonly formGroup = input.required<FormGroup>();
public readonly label = input.required<InputProps['label']>();
public readonly formControlName = input.required<InputProps['formControlName']>();
public readonly checkValidation = input<InputProps['checkValidation']>(false);
public readonly placeholder = input<InputProps['placeholder']>();
public type = input<InputProps['type']>('text');
public value = input<InputProps['value']>('');
}
input.component.html
<input
[type]="type()"
[id]="label()"
[formControlName]="formControlName()"
[ngClass]="inputBaseClasses"
[value]="value()"
/>
<label [for]="label()" [ngClass]="labelBaseClasses">
{{ label() }}
</label>
</div>
I'm using this in my AuthModule.ts
@NgModule({
declarations: [
LoginComponent,
RegisterComponent,
AuthComponent
],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
AuthRoutingModule,
AngularSvgIconModule.forRoot(),
ButtonComponent,
InputComponent
],
exports: [
FormsModule,
ReactiveFormsModule
]
})
export class AuthModule { }
app.route.ts
export const routes: Routes = [
{
path: '',
pathMatch: 'full',
component: AppComponent
},
{
path: 'auth',
loadChildren: () => import('@app/core/auth/auth.module')
.then(m => m.AuthModule)
}
];
I'm using app-input
component.
<app-input
label="Name"
formControlName="name"
[formGroup]="signUpForm"
></app-input>
I did try export FormsModule & ReactiveFormsModule from AuthModule.ts but still gives me no value accessor for form control : 'name'.
I am out of ideas right now.
When creating a custom form control, you generally do not need to feed in the formControlName
it exists on the component definition.
The custom component you create extends ControlValueAccessor
which takes care of the binding to form control.
The changes made to the code are as follows.
First we wrap the custom component inside the form with the formGroup binded. Also notice that we have added the form control name on the custom component.
<form [formGroup]="signUpForm">
<app-input
label="Name"
formControlName="name"
></app-input>
</form>
Then we create the custom component control, by referring the above links.
I removed the form control name from the component and just added an input event. The valueChanged, will store the updated value in the internal property _value
.
valueChanged(val: string) {
this.onChange(val);
}
When a change happens it's captured by input
and triggers the onChange
event of the control, which updates the actual form control.
When focus happens, I trigger the markAsTouched
event.
I bind the disabled
flag to the HTML, through attributes.
We can use writeValue
to bind the value from form control into the html.
writeValue(value: number) {
this._value = value;
}
import { Component, input } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import {
ReactiveFormsModule,
FormGroup,
FormsModule,
FormControl,
ControlValueAccessor,
NG_VALUE_ACCESSOR,
NgControl,
} from '@angular/forms';
import { CommonModule, NgClass } from '@angular/common';
type InputProps = {
formGroup: FormGroup;
type: 'text' | 'password' | 'number';
value: string;
label: string;
formControlName: string;
placeholder: string;
checkValidation: boolean;
};
@Component({
selector: 'app-input',
standalone: true,
imports: [FormsModule, ReactiveFormsModule, NgClass],
template: `
<input
[type]="type()"
#val
(focus)="markAsTouched()"
(input)="valueChanged(val.value)"
[disabled]="disabled"
[value]="_value"
/>
<label [for]="label()">
{{ label() }}
</label>
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: InputComponent,
},
],
})
export class InputComponent implements ControlValueAccessor {
public readonly label = input.required<InputProps['label']>();
public readonly checkValidation = input<InputProps['checkValidation']>(false);
public readonly placeholder = input<InputProps['placeholder']>();
public type = input<InputProps['type']>('text');
onChange = (value: any) => {};
_value!: any;
onTouched = () => {};
touched = false;
disabled = false;
writeValue(value: number) {
this._value = value;
}
valueChanged(val: string) {
this.onChange(val);
}
registerOnChange(onChange: any) {
this.onChange = onChange;
}
registerOnTouched(onTouched: any) {
this.onTouched = onTouched;
}
markAsTouched() {
if (!this.touched) {
this.onTouched();
this.touched = true;
}
}
setDisabledState(disabled: boolean) {
this.disabled = disabled;
}
}
@Component({
selector: 'app-root',
standalone: true,
imports: [ReactiveFormsModule, InputComponent, CommonModule],
template: `
<form [formGroup]="signUpForm">
<app-input
label="Name"
formControlName="name"
></app-input>
</form>
{{signUpForm.value | json}}
`,
})
export class App {
signUpForm = new FormGroup({
name: new FormControl('test'),
});
}
bootstrapApplication(App);