ERROR Error: No value accessor for form control with unspecified name attribute
If yes, this blog will help you to understand what that error is and how to resolve it. I used bootstrap dropdown as an example but the concept remains the same for all custom FormControl. Read the full article to use it on any customized FormControl.
Let’s first understand why Angular throws the error. Consider the following example of a simple input textbox.
// .ts inputFormControl = new FormControl('hello world', Validators.required); // .html <input type="text" [formControl]="inputFormControl">
The above code simply adds one input textbox and sets its value as “hello world”. All basic form elements like <input>
, <textarea>
, <select>
have its APIs to set value, to get value, to add validators like maxlength
, minlengh
, etc.
Angular implements these APIs behind the scene and gives us FormControl class to perform form related operations. In this case, Angular knows how to set the value of input textbox, how to validate if the input textbox has any value or not.
But in case of bootstrap dropdown or any other custom form element, Angular doesn’t know this stuff. A bootstrap dropdown is not a basic form element. It does not have its API to set or get the values. Instead, its styles are set in such a way that it works like a dropdown ( <select>
HTML tag ). Therefore, Angular throws the error.
Here’s How You can Resolve the Custom FormControl Error
This blog uses ngx-bootstrap ( an Angular wrapper of bootstrap ) dropdown so that we don’t have to use jQuery.
ng add ngx-bootstrap --component dropdowns
ng generate component custom-dropdown
Line-9: Define a property options
to hold the dropdown items.
Line-11: Define a property selectedOption
to hold the value of dropdown ( selected option ).
Line-16: Initialize the options
with an array of simple items.
Line-21: Initialize the selectedOption
with the first item of options
.
dropdown
, dropdownToggle
, *dropdownMenu
are simply directives of ngx-bootstrap.
Line-5: Show selectedOption
as the selected item.
Line-10: Iterate through all options
to show them as items of a dropdown.
To use custom-dropdown
as FormControl, it needs to implement the interface ControlValueAccessor
. This interface has three methods ( writeValue
, registerOnChange
, registerOnTouched
) which needs to be overridden by our CustomDropdownComponent
class.
Line-2: Implement interface ControlValueAccessor
Line-18, 20, 22: Define all three methods of ControlValueAccessor
as blank methods.
Line-11: Initialize dropdownControl
as new FormControl.
As soon as you save both files, Angular throws an error in the console. We will resolve that now.
writeValue()
method is called every time we set the value of a FormControl. It is the passing of value from .ts to .html file.
Line-18: Whatever value we set by dropdownControl.setValue('value')
is going to be passed in writeValue()
method as an argument. We use that argument to assign it to selectedOption
variable.
What is the way to get FormControl value every time it gets changed? As you know, it is
formControl.valueChange.subscribe((value) => {
// some operations with value
})
How can we do that with our custom form-control? In our case of a dropdown, we have to trigger the change in the value of FormControl when one clicks on an option.
As writeValue()
method passes the value from .ts to .html, registerOnChange()
method passes the value from .html to .ts. Let’s see how.
Line-7: Declaration of the function named onChange()
.
Line-24: registerOnChange()
method takes one argument which is a function. Whenever we want to notify that value is changed in FormControl we have to call this function. We assign the reference of this function to onChange
function. Now, we have to call onChange
function whenever we want to notify the change in the value of FormControl.
Line-28: changeSelectedOption()
method is called when one clicks on an option in the dropdown as we bind that method on click event of an option. In this method, we assign selectedOption
as option
argument, which is the clicked option. We call onChange()
function with option
as argument.
This method works the same way registerOnChange()
works. We registered a function onChange()
with the function provided in the argument of registerOnChange()
. Every time we change the value of dropdown we have to call onChange()
function. In the same way, we can declare one more function called onTouched()
which is registered with the function provided in the argument of registerOnTouched()
. Now, onTouched()
needs to be called every time form-control gets the touch event.
NG_VALUE_ACCESSOR
is a provider that specifies a class that implements ControlValueAccessor
interface. It is used by form directives to inject the value accessors.
Line-8: Pass the provider object to the providers array of the component.
And that’s it. As soon as you save the file, you see the error is gone and our dropdown works as FormControl.
Here’s the complete code.
Also Read: React vs Angular: Choosing the Perfect Framework for Your Next Project