How can I create custom component which would work just like native <input>
tag? I want to make my custom form control be able to support ngControl, ngForm, [(ngModel)].
As I understand, I need to implement some interfaces to make my own form control work just like native one.
Also, seems like ngForm directive binds only for <input>
tag, is this right? How can i deal with that?
Let me explain why I need this at all. I want to wrap several input elements to make them able to work together as one single input. Is there other way to deal with that? One more time: I want to make this control just like native one. Validation, ngForm, ngModel two way binding and other.
ps: I use Typescript.
You can also solve this with a @ViewChild directive. This gives the parent full access to all member variables and functions of an injected child.
See: How to access input fields of injected form component
There's an example in this link for RC5 version: http://almerosteyn.com/2016/04/linkup-custom-control-to-ngcontrol-ngmodel
We are then able to use this custom control as follows:
In fact, there are two things to implement:
ngModel
itselfControlValueAccessor
that will implement the bridge between this component andngModel
/ngControl
Let's take a sample. I want to implement a component that manages a list of tags for a company. The component will allow to add and remove tags. I want to add a validation to ensure that the tags list isn't empty. I will define it in my component as described below:
The
TagsComponent
component defines the logic to add and remove elements in thetags
list.As you can see, there is no input in this component but a
setValue
one (the name isn't important here). We use it later to provide the value from thengModel
to the component. This component define an event to notify when the state of the component (the tags list) is updated.Let's implement now the link between this component and
ngModel
/ngControl
. This corresponds to a directive that implements theControlValueAccessor
interface. A provider must be defined for this value accessor against theNG_VALUE_ACCESSOR
token (don't forget to useforwardRef
since the directive is defined after).The directive will attach an event listener on the
tagsChange
event of the host (i.e. the component the directive is attached on, i.e. theTagsComponent
). TheonChange
method will be called when the event occurs. This method corresponds to the one registered by Angular2. This way it will be aware of changes and updates accordingly the associated form control.The
writeValue
is called when the value bound in thengForm
is updated. After having injected the component attached on (i.e. TagsComponent), we will be able to call it to pass this value (see the previoussetValue
method).Don't forget to provide the
CUSTOM_VALUE_ACCESSOR
in the bindings of the directive.Here is the complete code of the custom
ControlValueAccessor
:This way when I remove all the
tags
of the company, thevalid
attribute of thecompanyForm.controls.tags
control becomesfalse
automatically.See this article (section "NgModel-compatible component") for more details:
Thierry's example is helpful. Here are the imports that are needed for TagsValueAccessor to run...
This is quite easy to do with
ControlValueAccessor
NG_VALUE_ACCESSOR
.You can read this article to make a simple custom field Create Custom Input Field Component with Angular
Why to create a new value accessor when you can use the inner ngModel. Whenever you are creating a custom component which has an input[ngModel] in it, we already are instantiating an ControlValueAccessor. And that's the accessor we need.
template:
Component:
Use as: