Creating a custom form field with validation using AngularJS 1.5 and TypeScript
The typical way to do field validation in AngularJS making use of the built in FormController would look something like the following:
In this sample, if the text input is edited and then left empty:
- the form group will be marked as red
- the warning message will be displayed
- the Submit button will be disabled
An excellent tutorial on making use of the built-in form validation can be found here.
Of course, adding more fields, the html would start to a look a little bit “busy” with all the additional validation boiler plate. And since I am working with AngularJS 1.5, I decided to turn it into a form field component.
It took quite a few variations to get to the solution. I was trying to make a component that would fulfill these criteria:
- Not involve the use of two-way binding. Though it is still possible to make use of two-way binding with Angular components (“=”) it is not recommend in the AngularJS documentation. The main reason being that a child should not be changing the parent’s data.
- The validation of the field should be handled by the component and not rely on the ngModel $invalid and $pristine for setting an error class or displaying warning messages.
- The validity of the form field should be communicated to the parent form to prevent data from being submitted by the user if any of the fields are invalid.
The result makes for much cleaner HTML:
The TypeScript is as follows:
and here’s the HTML for the view:
How it works
Instead of passing the field value through a binding (which would require either two-way binding or one-way binding with an event output) I make use of the require functionality to inject ngModel:
I am then able to interact with the ngModel controller to read and write the model value:
READ: self.value = self.model.$viewValue
and to set validity:
I made use of $onInit as this is run after the component has been rendered and the model is accessible and then implement a handler for $render as this is called whenever the model is changed.
But since the whole concept of Single Responsibility Principle seems to be something that is being embraced by the Angular community I would consider making new components to support this. That would also go for other field types, perhaps even making use of the inheritance that’s possible with TypeScript.