we all love two-way data bindings right?. we love when we add “NgModel” directive to some input and values are synced one way another. It’s a really cool feature. but things got’s interesting when we want to add this functionality to our custom component, for example, a component like this
we have two buttons, and when we click “plus” it increases by one when we click “minus” it decreases by one. we want to use this component all over the place, but also we want to use “NgModel” feature on this component.
let’s look at how our component looks like
now let’s try to use this component anywhere and add ngModel to it.
<header>Update your cash</header>
<app-input [(ngModel)]="cash"> </app-input></div>
okay we run, and get this error
hmmm, okay a bit confusing error. it says that value accessor ( we don’t know what’s that ) for form control (we don’t know that either ) with unspecified name attribute ( yeah we know name attribute) etc. it says blah blah… name attribute, so we might think that if we add name attribute everything will work. let’s try to add and see what happens
<header>Update your cash</header>
<app-input [(ngModel)]="cash" name="cash"> </app-input></div>
If we run it we will have the next result
okay and now we are lost. “No value accessor for form control with the name: “cash”, even harder, than the previous one.
as we fill find out, there is nothing difficult there, but before I explain how to fix this error, let me show you how angular/forms work under the hood and why it needs this value accessor.
Control Value Accessor
every native HTML input you use inside angular (input,textarea, select, checkbox), it uses directive, that implements Control Value Accessor interface to work with ngModel. it means that this directive is responsible for writing data from the model to view and vice versa, so whenever you use the simplest input like this,
Angular adds a directive to this input behind the scenes. whenever update is coming from the model, ngModel says “hey control value accessor here’s a new value from a model, get it…”, Control Value Accessor updates the view and whenever a user changes something, Control Value Accessor says to ngModel, “hey here’s a new value from user grab it”.
for the sake of simplicity, I will show you how actually angular adds directive, so you guess nothing magic goes there.
That’s Angular source code ( Actually I deleted comments for a shrinking purpose)
let me clear this up, every time angular sees input that is number type and has also ngModel attribute with it, adds this directive to this input. this directive implements ControlValueAccessor interface, that has 4 methods to implement
the most important ones are WriteValue, RegisterOnChange
this method is called whenever a value is changed outside, for example, we changed cash amount to 5 from app.component, then this method is called, has a value “5” as a parameter, and this directive updates input value accordingly.
It looks a bit complicated, but It’s as simple as WriteValue. RegisterOnChange function is called once per Initialize and you just have to save the reference of the passed function, so whenever a value is changing from view, you call that function to pass the new value to it, and ngModel will be updated accordingly.
so for now, we know that, to add ngModel to our custom component, we have to implement ControlValueAccessor. the last thing we have to do is to add this component to “NG_VALUE_ACCESSOR” providers array. that’s because whenever ngModel directive is initialized, it uses NG_VALUE_ACCESSOR providers array to fetch current value accessor he is working on (for example NumberValueSelector, SelectValueAccessor etc.), here’s ngModel constructor
so directive is added to NG_VALUE_ACCESSOR like this
Custom Form Control
okay, at final let’s create our custom form control
now whenever we use our component we can just add ngModel directive to it like this