Fanis Prodromou
  • Blog
  • About
  • Blog
  • About
Search by typing & pressing enter

YOUR CART

12/16/2019 Comments

Angular - Custom Form Elements

Picture
Photo by Eugen Str on Unsplash
The majority of the products I worked on was/is B2B and they consisted of many (MANY) web forms. You know how this goes.
  • You have to create the form model in the component (especially if you use reactive forms)
  • Create the HTML elements
  • Create the HTML elements
  • ...
  • Create the HTML elements
Those HTML elements more or less have a wrapper <div> element, a <label> tag, an <input> field, elements for error messages. This goes on and on. 

Who is our best friend for situations like this? ctrl+c & ctrl+v :) Yeah I know. I've been there.

Someone might say that this "best friend" helps us in many different cases. Here a bell rings. What do you do when you have to re-use the same code again and again? The answer is Abstraction. 
​

You know how to abstract your code in JS, in TS in C# and in many other languages. What about HTML though? Continue reading to find this out :) 

What to build

Let's illustrate what we'd like to build
Picture
It doesn't seem hard. Let's see the code before any abstraction
component.ts

    
component.html

    
The above HTML has 4 form controls along with their error messages. Let's improve it, minimize the code and make it more fun :) and easier to maintain

Create Your Custom Form Control

First things first.
​
​1. Create a component​

    
2. Tell Angular that this component should work with Template Driven and Reactive Forms

    
We registered the providers and implemented a new interface.

The 
NG_VALUE_ACCESSOR used to provide a ControlValueAccessor for form controls.

The interface ControlValueAccessor is used by Angular to sync with formControl and ngModel, and provides the following methods:
  • writeValue
  • registerOnChange
  • regsiterOnTouched
  • setDisabledState
 3. Let's add some HTML in our newly created component
Our goal is to remove as much repeated code from HTML as it could be. As such, we are gonna remove all the code that is wrapped in the <div class="form-group">. Hence our custom form element component will have the wrapper div and its children

    
The variables that we used in the template are just @Input() fields and a public one :)

    
4. Let's give it a try and use the component in our form.
At this point we have just a form control with no binding in our form model. Hold on your horses

    
We commented out the old code and we replaced it with the new component selector
5. Binding with form model
In order to bind your form control with the component, or in other words to make it work with formControlName or ngModel​ you have to utilize the writeValue method.

According to Angular's documentation
The writeValue method "​Writes a new value to the element."
This is the fun part of our code. We are going to use a form field that is not immediately connected with our initial form that we've created in our form component. Wait, what? Let's see the code :) 

    

    
Create a field and bind it in your HTML.
The diagram below depicts the data flow
Picture

** A friend of mine asked the following: "when the writeValue is been triggered?"

I won't go with the component life-cycle approach, but simply by saying that when the formControl has a value, firstName:['foo'], this value ("foo" in this example) will pass in the custom component and trigger the writeValue method.

Can I use now the formControlName? Ohh Yeah!

    
Up to that point we managed to pass the value from the main component to the custom form control. It's one way data binding. We now have to create two way data binding as we want whatever the user writes in our custom input control to go back in our formGroup -> formControl.
​
I promise that it won't last long and soon you will have your first custom control :)
6. Two way data binding
In order to implement a two way data binding ​ you have to utilize the registerOnChange method

According to Angular's documentation
The registerOnChange "Registers a callback function that is called when the control's value changes in the UI."

    

    
Hurrah!! 
Up to this point we created our custom Form Element and works as a charm.

I hear your whisper.... "what about the validation messages?" If you want to implement the validation messages continue reading, otherwise you can see the complete code in this repo ​https://github.com/profanis/angular-custom-form-elements/tree/custom-form-no-validation
7. Validation messages
The best approach to accomplish this is to have embedded validations using the NG_VALIDATORS Provider. (We won't cover this here)

If however you want to make it work with the bare minimum changes in your code, you can pass in the custom form control the FormControl as @Input()

    
You can display the error messages iterating the result of getErrors() method 

    

Final Code


    

Repo

You can find all the code in this repo https://github.com/profanis/angular-custom-form-elements
  • In the branch  custom-form-no-validation you will find the code without the validation messages
  • In the branch  custom-form-with-validation you will find the code with the validation messages

Thanks for spending your time reading my article. I hope you enjoyed it! :)
Comments
comments powered by Disqus

    Author

    I am an Angular GDE, content creator who loves sharing knowledge

    Follow @prodromouf
    View my profile on LinkedIn

    Tags

    All Angular Angularjs General Graphql Ionic Javascript .net Nodejs Typescript

Proudly powered by Weebly