ERROR Error: No value accessor for form control with unspecified name attribute
Custom FormControl error
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
1//.ts
2inputFormControl = new FormControl('hello world', Validators.required);
1// .html
2<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.
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
1formControl.valueChange.subscribe((value) => {
2 // some operations with value
3})
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