Sep 22, 2016

Angular Form Validation

Igor Tokarev
Igor Tokarev


Display errors after edit

One of the key features of form validation in Angular is an error displaying. When we create a form in view template, Angular creates a controller for the form. This controller contains controls and nested forms with controls statuses (valid/invalid), errors etc.

The default behavior of controller is to set errors for controls with invalid values immediately when the form is displayed. F.e. a form with some mandatory fields was loaded on a web page and these fields are empty by default, the form is waiting for user data. The controller sets errors for these mandatory fields because no value is also an invalid value. But it’s better to show these errors to user only on their actions like field editing or trying to send/save data, not at once an empty form was shown.

To display errors we use a combination of control states: $dirty && $invalid, where $dirty means control data was changed by user, $invalid indicates that the model has invalid values.

<!-- component template -->
    <form name="testForm">
        <input type="text" name="field1" ng-model="obj.field1" required>

        <div ng-messages="testForm.field1.$error" ng-messages-include="messages.html" ng-show="testForm.field1.$dirty"></div>

        <button type="submit">Send</button>
<!-- messages.html -->
    Field is required.

In this example, we use only one directive “required” to check if the field is filled, and ngMessages module to display errors. However, the form displays an error only if field1 was edited by user: ng-show="testForm.field1.$dirty". This means if a user clicks Send button right away with no data inputted to the form, there won’t be any error shown. It’s necessary to set property $dirty for the control, we use $setDirty() method for this.

Here’s a small service to set this property:

app.service("formValidation", ->
        setDirtyField = (field) ->
            # set $dirty property for the field

        localValidateForm: (form) ->
            # angular form validate
            # there can be nested forms therefore we should repeat the action recursively
            _formValidate = (innerForm) ->
                # check every form attribute angular.forEach
                angular.forEach innerForm, (field, name) ->
                    # check if current attribute is a control or nested form
                    # in this case attribute name doesn’t have “$” in the beginning
                    if angular.isString(name) and !name.match('^[\$]')
                        setDirtyField field
                        # check if attribute has method $setViewValue
                        # this allows to identify whether it is a control or a nested form
                        # if there’s no $setViewValue method then this is a nested form
                        if not angular.isFunction(field.$setViewValue)
                            _formValidate field
            _formValidate form

This is how the service can be used: formValidation.localValidateForm(scope.testForm). For convenience of use I created a directive which sets property $dirty on Submit action to all form fields and calls event handler if the form is valid:

app.directive('submit', ['$parse', 'formValidation', ($parse, formValidation) ->
        restrict: 'A'
        link: (scope, formElement, attrs) ->
            # link to Submit action of the form
            formElement.bind 'submit', (e) ->
                # get the form from current element
                form = scope[] or scope.$eval( or scope.$eval(attrs.ngForm)
                # stops submit action
                # sets $dirty with our service
                formValidation.localValidateForm form
                # call $digest loop to watch errors
                if form.$valid
                    # call handler that we passes to the function
                    fn = $parse(attrs.submit)
                    scope.$apply(-> fn(scope, {$event: e}))

An example:

<form name="testForm" submit="submitForm()">
        <input type="text" name="field1" ng-model="obj.field1" required>
        <div ng-messages="testForm.field1.$error" ng-messages-include="messages.html" ng-show="testForm.field1.$dirty"></div>
        <button type="submit">Send</button>

On Send button click the testForm will show errors in case of invalid data, otherwise, action submitForm() will be called.

There are some issues with form validation in Angular 1. The entire validation system is based on rendered input fields and this leads to next disadvantages:

  • We can’t reuse validation without view render. We got this issue in one of our projects, where we needed to validate data stored in the controller and there was no possibility to display form template. We had to duplicate validation in the controller to solve the problem.
  • A complexity of tests. Developers have to use end-to-end tests.

Angular form validation in controller

Below I describe validation solution with use of Angular 1. In the example I use a small library for data validation - validatejs. This library provides a declarative way to validate js objects.

/ Requirements declaration:
    var constraints = {
        username: { presence: true },
        password: {
            presence: true,
            length: {
                minimum: 6,
                message: "must be at least 6 characters"

    // Validation example:

    validate({password: "bad"}, constraints);
    // => {
    //   "username": ["Username can't be blank"],
    //   "password": ["Password must be at least 6 characters"]
    // }

    // Validation of single value:
    validate.single("foo", {presence: true, email: true});
    // => ["is not a valid email"]

The validation function returns a list of errors for invalid data and undefined for valid data.

The library has several stock validators like DateTime, Email, Equality, Length, URL etc, and you can add your custom one. After checking the form with validatejs we display it and call formValidation.localValidateForm(testForm) to show errors.

Example with the same form:

<div ng-controller="TestCtrl as $ctrl">
        <button type="button" ng-click="$ctrl.checkData()">Check data</button>
        <form name="$ctrl.testForm" submit="$ctrl.submitForm()" ng-if="$ctrl.showTestForm">
            <input type="text" name="field1" ng-model="$ctrl.obj.field1" required>
            <div ng-messages="testForm.field1.$error" ng-messages-include="messages.html" ng-show="$ctrl.testForm.field1.$dirty"></div>
            <button type="submit">Send</button>
    app.controller('TestCtrl', ['$timeout', 'formValidation', ($timeout, formValidation) ->
        ctrl = @
        ctrl.obj = {field1: ''}
        ctrl.showTestForm = false
        # requirements for the form
        constraints = field1: { presence: true }
        ctrl.checkData = ->
            # validation
            if validate(ctrl.obj, constraints)
                # if values are invalid then show form and errors
                ctrl.showTestForm = true
                # timeout is necessary as the form controller is not available right away
                $timeout((-> formValidation.localValidateForm(ctrl.testForm)), 0)

        ctrl.submitForm = ->
            alert('Data ' + ctrl.obj.field1)

The form is not displayed in this example because ng-if condition is not satisfied. On Check button click I call function ctrl.checkData in the controller to validate our values, and if they are invalid the function returns a list of errors.


More thoughts

Jun 27, 2018Technology
How to Work With Legacy Code: Code Refactoring Techniques

In this article we'll review general approach to working with the best kind of projects - the ones with old untested and undocumented spaghetti code and a tight schedule. We'll review anger management techniques, coping mechanisms and some refactoring tips that might come in handy.

Vladimir Sidorenko
Vladimir Sidorenko
Jun 14, 2017Technology
How to Deploy a Django Application on Heroku?

In this article I'll show you how to deploy Django with Celery and Postgres to Heroku.

Vladimir Kalyuzhny
Vladimir Kalyuzhny
May 10, 2018Technology
How to Build a Cloud-Based Leads Management System for Universities

Lead management is an important part of the marketing strategy of every company of any size. Besides automating various business processes, privately-held organizations should consider implementing an IT solution that would help them manage their leads. So, how should you make a web-based leads management system for a University in order to significantly increase sales?

Vladimir Sidorenko
Vladimir Sidorenko
May 18, 2017Technology
Angular2: Development Tips and Trick

In this article we'll discuss some tricks you can use with Angular to make routing cleaner and improve SEO of your application.

Alexandr Selyanov
Alexandr Selyanov
Nov 21, 2016Technology
Crawling FTP server with Scrapy

Welcome all who are reading this article. I was given a task of creating a parser (spider) with the Scrapy library and parsing FTP server with data. The parser had to find lists of files on the server and handle each file separately depending on the requirement to the parser.

Rostyslav Stekh
Rostyslav Stekh
Sep 23, 2010Technology
Dynamic class generation, QuerySetManager and use_for_related_fields

It appears that not everyone knows that in python you can create classes dynamically without metaclasses. I'll show an example of how to do it.So we've learned how to use custom QuerySet to chain requests:Article.objects.old().public()Now we need to make it work for related objects:user.articles.old().public()This is done using use_for_related_fields, but it needs a little trick.

Vladimir Sidorenko
Vladimir Sidorenko