Validating a Form using Angular Validation Directives

Posted by: Mahesh Sabnis , on 2/10/2015, in Category ASP.NET MVC
Views: 30439
Abstract: This article demonstrates how to use Angular Validation Directives to fulfil validation requirements for a web application.

In all versions of ASP.NET, we have had support for server-side Validation Controls that gets executed when the PostBack takes place. In ASP.NET MVC, validations can be checked using DataAnnotations by applying validation rules and error messages on the model properties.

On the other hand, it is also possible to use a script in the page that performs validation on the data in the form before it is posted back to the server. This is called client-side validation.

In the current era of web applications, various JavaScript plug-ins have been created that supports client-side validations. E.g. jQuery Validation plugin, Angular Validation directives and so on. In this article, we will be exploring validation directives provided in the Angular.js Framework. The Angular version used in this article is 1.3+.

 

In Angular, we use HTML <input> for data binding, validation etc. To validate the <input>, we can make use of the following attribute Directives:

  • ng-required : Makes an entry in the field mandatory. This sets the HTML required attribute to true.
  • ng-minlength: The min required length of the text in the <input> text element.
  • ng-maxlength: The max length of the text in <input> text element.
  • ng-pattern: The regular expression based input in the <input> text element.

Since in Angular, a major focus is on the ngModel used for DataBinding, we can configure the validation check when the DataBinding gets executed. We can do this by using the Angular validation classes listed below:

  • ng-valid: Applies when the model is valid.
  • ng-invalid: Applies when the model is invalid.
  • ng-dirty: Applies when the UI element is changed or interacted with.
  • ng-touch: Applies when the element has lost focus (JavaScript Blur).
  • ng-untouched: Applied when the element has not lost focus yet.

To implement these classes, we will be using the same demo that we used in Angular Form with Bootstrap application.

Step 1: Open Visual Studio 2013 (the article is written using VS2013 with Update 4). Open the downloaded solution in Visual Studio. In the project, we have the following files:

  • pModule.js
  • pService.js
  • pDirective.js
  • pController.js
  • pTemplate.html

Step 2: Open pTemplate.html and review the element structure. We need to make some changes in the <form> element. Add the name attribute to the form and set its name as personForm. This is necessary because we need to apply validations on the form elements and we need to refer each element using its name attribute. To apply validations we will refer each form element as:

<formName>.<elementName>

We already have name attribute set for each <input> element, <textarea> and <select>.

In the form, add the novalidate attribute which will prevent the browser’s in-built validation feature when the form is submitted. This is the arrangement we are doing to execute the Angular validations we will be setting shortly.

Step 3: Modify pTemplate.html and enclose each <label> and its corresponding <input> element in <div> with class as form-group as below:

<div class="form-group">   
    <label for="personId" class="col-sm-3 control-label">Person Id</label>
    <div class="col-sm-9">
        <input type="text" id="personId" name="personId"
               ng-model="PersonId" class="form-control" readonly="readonly" />
    </div>
</div>

Repeat the same step for all input elements e.g. FirstName, MiddleName, LastName. Similarly create separate form-group for <textarea>, <input type=’radio’>, etc. This is required so that we can separately apply validation for each element along with its validation error message.

Step 4: Apply Angular Validation on elements as shown here:

<form class="form-horizontal" name="personForm" novalidate role="form">
    <fieldset>
        <legend>Person Basic Info</legend>
        <div class="form-group">
           
            <label for="personId" class="col-sm-3 control-label">Person Id</label>
            <div class="col-sm-9">
                <input type="text" id="personId" name="personId"
                       ng-model="PersonId" class="form-control" readonly="readonly" />
            </div>
        </div>
        <div class="form-group">
            <label for="title" class="col-sm-3 control-label">Person Id</label>
            <div class="col-sm-9">
                <select id="title" name="title" class="form-control"
                        ng-model="selectedtitle"
                        ng-options="t for t in title"></select>
            </div>
        </div>  
            
        <div class="form-group" ng-class="{'has-error':personForm.firstName.$invalid && personForm.firstName.$dirty}">
            <label for="firstName" class="col-sm-3 control-label">First Name</label>
            <div class="col-sm-9">
                <input type="text" id="firstName" name="firstName"
                       ng-model="FirstName" class="form-control" ng-required="true" 
                      ng-maxlength="20" ng-pattern="/^[A-Z]/"/>
                <span class="help-block" ng-if="personForm.firstName.$error.required && personForm.firstName.$dirty">First Name is Must</span>
                <span class="help-block" ng-if="personForm.firstName.$error.pattern">Must start from Capital letter</span>
                <span class="help-block" ng-if="personForm.firstName.$error.maxlength">Must be less than 20 letters</span>
            </div>
        </div>
            
        <div class="form-group" ng-class="{'has-error':personForm.middleName.$invalid && personForm.middleName.$dirty}">
            <label for="middleName" class="col-sm-3 control-label">Middle Name</label>
            <div class="col-sm-9">
                <input type="text" id="middleName" name="middleName"
                       ng-model="MiddleName" class="form-control" ng-required="true"
                       ng-maxlength="20" ng-pattern="/^[A-Z]/" />
                <span class="help-block" ng-if="personForm.middleName.$error.required && personForm.middleName.$dirty">Middle Name is Must</span>
                <span class="help-block" ng-if="personForm.middleName.$error.pattern">Must start from Capital letter</span>
                <span class="help-block" ng-if="personForm.middleName.$error.maxlength">Must be less than 20 letters</span>
            </div>
        </div>
            
        <div class="form-group" ng-class="{'has-error':personForm.lastName.$invalid && personForm.lastName.$dirty}">
            <label for="lastName" class="col-sm-3 control-label">Last Name</label>
            <div class="col-sm-9">
                <input type="text" id="lastName" name="lastName"
                       ng-model="LastName" class="form-control" ng-required="true"
                       ng-maxlength="20" ng-pattern="/^[A-Z]/" />
                <span class="help-block" ng-if="personForm.lastName.$error.required && personForm.lastName.$dirty">Last Name is Must</span>
                <span class="help-block" ng-if="personForm.lastName.$error.pattern">Must start from Capital letter</span>
                <span class="help-block" ng-if="personForm.lastName.$error.maxlength">Must be less than 20 letters</span>
            </div>
        </div>       
              
            <div class="form-group">
                <label for="gender" class="col-sm-3 control-label">Gender</label>
                <div class="col-sm-9">
                    <select id="gender" name="gender" class="form-control"
                            ng-model="selectedGender"
                            ng-options="g for g in gender"></select>
                </div>
            </div>                  

        <div class="form-group"
             ng-class="{'has-error':personForm.birthdate.$invalid && personForm.birthdate.$dirty}">
            <label for="birthdate" class="col-sm-3 control-label">Birth Date</label>
            <div class="col-sm-9">
    
                <input type="text" class="form-control" name="birthdate" id="birthdate"
                       datepicker-popup="dd/mm/yyyy"
                       ng-model="BirthDate" is-open="opened"
                       close-text="Close" />
                <span class="help-block" ng-show="personForm.birthdate.$error.required && personForm.birthdate.$dirty">Please Select Date</span>
            </div>
        </div>                  
    </fieldset>

    <fieldset>
        <legend>Address Information :</legend>
        <div class="form-group"
             ng-class="{'has-error':personForm.currentaddress.$invalid && personForm.currentaddress.$dirty}">
            <label for="currentaddress" class="col-sm-3 control-label">Current Address</label>
            <div class="col-sm-9">
                <textarea id="currentaddress" name="currentaddress"
                          ng-model="CurrentAddress" class="form-control" ng-required="true"></textarea>
                <span class="help-block" ng-show="personForm.currentaddress.$error.required && personForm.currentaddress.$dirty">Address us Must</span>
            </div>
        </div>
        <div class="form-group">
            <label for="issameaddress" class="col-sm-3 control-label">Check is Same Address:</label>
            <div class="col-sm-9">
                <input type="checkbox" id="issameaddress"
                       name="issameaddress" ng-model="IsSameAddress"
                       class="form-control"
                       ng-change="SameAddress()" />
            </div>
         </div>
        <div class="form-group"
             ng-class="{'has-error':personForm.permanentaddress.$invalid && personForm.permanentaddress.$dirty}">
            <label for="permanentaddress" class="col-sm-3 control-label">Permanent Address</label>
            <div class="col-sm-9">
                <textarea id="permanentaddress" name="permanentaddress"
                          ng-model="PermanentAddress" class="form-control"
                          ng-disabled="DisablesPermanentAddress" ng-required="true"></textarea>
                <span class="help-block" ng-show="personForm.permanentaddress.$error.required && personForm.permanentaddress.$dirty">Address us Must</span>
            </div>
        </div>
    </fieldset>

    <fieldset>
        <legend>Professional Information </legend>
        <div class="form-group">
            <div class="col-sm-3">
            </div>
            <div class="col-sm-9">

                <div class="radio">
                    <label>
                        <input type="radio" id="employeed" value="Employeed"
                               name="jobtype"
                               ng-model="Occupation" />Employeed
                    </label>
                </div>
                <div class="radio">
                    <label>
                        <input type="radio" id="selfemployeed"
                               ng-model="Occupation" name="jobtype" value="Self-Employeed" />Self-Employeed
                    </label>
                </div>
            </div>
        </div>
    </fieldset>

    <fieldset>
        <legend>Contact Infromation</legend>
        <div class="form-group"
             ng-class="{'has-error':personForm.email.$invalid && personForm.email.$dirty}">

            <label for="email" class="col-sm-3 control-label">Email</label>
            <div class="col-sm-9">
                <input type="email" id="email" name="email"
                       ng-model="Email" class="form-control" ng-required="true" />
                <span class="help-block" ng-if="personForm.email.$error.required"></span>
                <span class="help-block"
                      ng-if="personForm.email.$dirty && personForm.email.$error.email">valid email required</span>
            </div>
        </div>
        <div class="form-group" ng-class="{'has-error':personForm.mobileno.$invalid && personForm.mobileno.$dirty}">
            <label for="mobileno" class="col-sm-3 control-label">Mobile No.</label>
            <div class="col-sm-9">
                <input type="number" id="mobileno" name="mobileno"
                       ng-model="MobileNo" class="form-control"
                       ng-minlength="12" ng-pattern="/^[0-9]{1,12}$/" ng-required="true" />
                <span class="help-block"
                      ng-if="personForm.mobileno.$dirty && personForm.mobileno.$error.required">valid mobile no required</span>
                <span class="help-block" ng-if="personForm.mobileno.$error.pattern">Must be 12 digits</span>
                <span class="help-block" ng-if="personForm.mobileno.$error.minlength">Must be 12 in length (Country Code)</span>
                
            </div>
        </div>
    </fieldset>


    <div class="col-sm-offset-3 col-sm-9">
        <input type="button" class="btn btn-default" value="Cancel"
               ng-click="cancelForm()" />
        <input type="button" class="btn btn-primary" value="Submit"
               ng-click="submitForm()"  ng-disabled="personForm.$invalid"/>
    </div>

</form>

The above HTML markup shows the Angular Validation applied on each elements. The pattern for applying validations is as follows:

ng-class="{'has-error':personForm.firstName.$invalid && personForm.firstName.$dirty}"

ng-class is the Angular directive used to apply class on the elements based upon the evaluated expression. The expression {'has-error':personForm.firstName.$invalid && personForm.firstName.$dirty}", checks the validation on firstName using $invalid and $dirty. This means that if the firstName field is changed and if the value is invalid as per the Angular validation directive e.g. ng-required, ng-maxlength, ng-pattern, etc. then the error class will be applied. Since we have several validation checks on element, we need to display corresponding error messages e.g. separate error message for maxlength, required, pattern etc. To implement it, we need to define separate <span> elements in the markup code as shown here:

<span class="help-block" ng-if="personForm.firstName.$error.required && personForm.firstName.$dirty">First Name is Must</span>
<span class="help-block" ng-if="personForm.firstName.$error.pattern">Must start from Capital letter</span>
<span class="help-block" ng-if="personForm.firstName.$error.maxlength">Must be less than 20 letters</span>

The above markup uses ng-if directive on <span>, this indicates that only a specific error message will be displayed when the corresponding error message occurs.

On the form, we have email and mobile fields. The email is defined as <input> type = email and mobile field is defined as <input> type = Number. The email is set to the pattern default to HTML email type and mobile is set to the following pattern:

ng-pattern="/^[0-9]{1,12}$/"

The Mobile numbers can accept only 12 digits (includes country code).

Since we need to disable the Submit button when all validations are satisfied, we can achieve it using ng-disabled Angular directive as shown here:

ng-disabled="personForm.$invalid"

This directive accepts Boolean value, the value will be true only if the form is validated. Once the form has all valid entries, the Submit button will be enabled.

Step 5: Run the application, the page will displayed with disabled submit button. Try to add value in the First Name and remove it, the validation will be executed and an Error message will be displayed as shown here:

angular-fname-valid

Note: With Angular 1.3 and ng-touched, as soon as the control has blurred, you can now set a style on it, regardless of whether the value in the control was actually edited or not.

The above shows an error message for the FirstName as it must start with a Capital letter. Validations on email and mobile fields are shown as below:

angular-emailandmobile

Note that the Submit button will be enabled only when all entries are valid.

Conclusion: As we saw, validation requirements for a web application can be easily fulfilled using Angular Validation Directives.

 

Download the entire source code of this article (Github)

This article has been editorially reviewed by Suprotim Agarwal.

Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Explore the Table of Contents or Download Sample Chapters!

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+

Author
Mahesh Sabnis is a DotNetCurry author and a Microsoft MVP having over two decades of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions), and Front-end technologies like Angular and React. Follow him on twitter @maheshdotnet or connect with him on LinkedIn


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!