Click here to Skip to main content
15,887,596 members
Articles / Web Development / ASP.NET
Tip/Trick

Simple ASP.NET MVC Validation Hack

Rate me:
Please Sign up or sign in to vote.
4.88/5 (6 votes)
18 Jan 2015CPOL2 min read 22.2K   10   7
Customising ASP.NET MVC client side validation to validate form in parts

Introduction

Sometimes, you might come across a problem where you have to validate form in parts (for wizard) at client side without a round trip to server. For that in ASP.NET MVC framework, you have various options like doing custom validations using JavaScript, jQuery or Ajax postback.
In this hack/tip, we will be mixing server side validation declaration with client side and in parts.

Using the Code

To demonstrate, I will be creating a simple registration form wizard. In the first page, we capture basic details like first name, display name, etc., contact details on second page and miscellaneous details in the last page.

Let's start by creating Model (or ViewModel):

C#
public class PersonDetailsViewModel
{
    //Basic details
    [Required(ErrorMessage = "Please enter First name")]
    [Display(Name = "First Name")]
    public string FirstName { get; set; }

    [Display(Name = "Last Name")]
    public string LastName { get; set; }

    [Display(Name = "Display Name")]
    [MinLength(8, ErrorMessage = "Display name should be minimum of 8 characters")]
    public string DisplayName { get; set; }

    [Required(ErrorMessage = "Please enter Email address")]
    [RegularExpression(@"^\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$", 
        ErrorMessage = "Please enter valid email address")]
    [Display(Name = "Email")]
    public string Email { get; set; }

    //Contact details
    [Required(ErrorMessage = "Please enter address line 1")]
    [Display(Name = "Address line 1")]
    public string AddressLine1 { get; set; }

    [Display(Name = "Address line 2")]
    public string AddressLine2 { get; set; }

    [Required(ErrorMessage = "Please enter mobile number")]
    [RegularExpression(@"^([0-9]( |-)?)?(\(?[0-9]{3}\)?|[0-9]{3})( |-)?
    ([0-9]{3}( |-)?[0-9]{4}|[a-zA-Z0-9]{7})$", ErrorMessage = "Please enter valid mobile number")]
    [Display(Name = "Mobile")]
    public string Mobile { get; set; }

    [Display(Name = "Phone")]
    public string Phone { get; set; }

    [Required(ErrorMessage = "Please enter Post Code")]
    [Display(Name = "Post code")]
    public string PostCode { get; set; }

    //Other Details
    [Required(ErrorMessage = "Please enter Date of birth")]
    [Display(Name = "Date of birth")]
    public DateTime DOB { get; set; }

    [Display(Name = "Other details")]
    public string Other { get; set; }
}

As you can see, we have Model divided (grouped) into 3 parts, which will become separate pages(panels) in wizard.

Now in the controller, a simple check to check if its valid.

C#
public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Register()
    {
        if(ModelState.IsValid)
        {

        }
        return View("Index");
    }
}

Let’s move on to the View where all magic happens of validation.

HTML
@*Basic details panel*@
<div id="BasicDetailsPnl">

<div class="container">
<div class="form-group">
@Html.LabelFor(model => model.FirstName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger" })
</div>
</div>

<div class="form-group">
@Html.LabelFor(model => model.LastName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.LastName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" })
</div>
</div>

<div class="form-group">
@Html.LabelFor(model => model.DisplayName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.DisplayName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.DisplayName, "", new { @class = "text-danger" })
</div>
</div>

<div class="form-group">
@Html.LabelFor(model => model.Email, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Email, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Email, "", new { @class = "text-danger" })
</div>
</div>
</div>

<div class="nav">
<input type="button" value="Next" 
class="btn btn-default" onclick="validateBasicDetails();"/>
</div>

</div>

@*Contact details panel*@
<div id="ContactDetailsPnl">

<div class="container">
<div class="form-group">
@Html.LabelFor(model => model.AddressLine1, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.AddressLine1, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.AddressLine1, "", new { @class = "text-danger" })
</div>
</div>

<div class="form-group">
@Html.LabelFor(model => model.AddressLine2, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.AddressLine2, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.AddressLine2, "", new { @class = "text-danger" })
</div>
</div>

<div class="form-group">
@Html.LabelFor(model => model.Mobile, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Mobile, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Mobile, "", new { @class = "text-danger" })
</div>
</div>

<div class="form-group">
@Html.LabelFor(model => model.Phone, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Phone, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Phone, "", new { @class = "text-danger" })
</div>
</div>

<div class="form-group">
@Html.LabelFor(model => model.PostCode, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.PostCode, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.PostCode, "", new { @class = "text-danger" })
</div>
</div>
</div>

<div class="nav">
<input type="button" value="Prev" 
class="btn btn-default" onclick="showBasicDetailsPnl();" />
<input type="button" value="Next" 
class="btn btn-default" onclick="validateContactDetails();" />
</div>

</div>

@*Other details panel*@
<div id="OtherDetailsPnl">

<div class="container">
<div class="form-group">
@Html.LabelFor(model => model.DOB, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.DOB, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.DOB, "", new { @class = "text-danger" })
</div>
</div>

<div class="form-group">
@Html.LabelFor(model => model.Other, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Other, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Other, "", new { @class = "text-danger" })
</div>
</div>
</div>

<div class="nav">
<input type="button" value="Prev" 
class="btn btn-default" onclick="showContactDetails();"/>
<input type="submit" value="Finish" class="btn btn-default"/>
</div>

</div>

As you can see, we have 3 main divs representing major parts Model. I have scaffolded the view using default Visual Studio menu and modified by separating as per the model. Now by adding simple Previous and Next buttons, we can create a registration wizard.

JavaScript
//Get references to panel 
var basicDetailsPnl = $("#BasicDetailsPnl");
var contactDetailsPnl = $("#ContactDetailsPnl");
var otherDetailsPnl = $("#OtherDetailsPnl");
var form = $('form');

//hide the panel at page load
$(document).ready(function () {
    contactDetailsPnl.hide();
    otherDetailsPnl.hide();
});

//Next - Basic
function validateBasicDetails() {

    var isValid = form.validate().element($('#FirstName'))
        & form.validate().element($('#Email'));

    if (isValid) {
        contactDetailsPnl.show();
        basicDetailsPnl.hide();
    }

    return isValid;
}

//Prev - Contact Details
function showContactDetails() {
    otherDetailsPnl.hide();
    contactDetailsPnl.show();
};

//Next - Contact Details
function validateContactDetails() {
    
    var isValid = form.validate().element($('#AddressLine1'))
        & form.validate().element($('#PostCode'));

    if (isValid) {
        otherDetailsPnl.show();
        contactDetailsPnl.hide();
    }

    return isValid;
}

//Prev - Other Details
function showBasicDetailsPnl() {
    contactDetailsPnl.hide();
    basicDetailsPnl.show();
};

In JavaScript functions validateBasicDetails() and validateContactDetails(), we have a variable boolean isValid which is assigned the value of validation result for the controls we have in that particular div. If all the controls are valid as per the validations assigned to Model, the value of variable isValid will be true. For last div OtherDetailsPnl, the validation will be done using Finish button, which is, a default submit action for page so you don't need a separate JavaScript function.

As you can see, in JavaScript functions we can validate the individual control by calling validate method of form on specific controls we want to validate. To create wizard, we can group controls and perform “AND” operation to group the result to create a wizard.

That’s it! We are done, if you run and see the wizard in browser, we can validate controls separately for each div.

Hope this trick will help. If you want to improve the code or have suggestions, please send a pull request on git repository.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralExcellent work. Pin
Sachin Makwana3-Jan-16 22:59
professionalSachin Makwana3-Jan-16 22:59 
QuestionIsn't form.validate() enough? Pin
jtpatil20-Jan-15 8:18
jtpatil20-Jan-15 8:18 
AnswerRe: Isn't form.validate() enough? Pin
Prashant Kurlekar20-Jan-15 11:05
Prashant Kurlekar20-Jan-15 11:05 
QuestionWhy is it a hack? Pin
Adam Tibi20-Jan-15 4:40
professionalAdam Tibi20-Jan-15 4:40 
AnswerRe: Why is it a hack? Pin
Prashant Kurlekar20-Jan-15 11:07
Prashant Kurlekar20-Jan-15 11:07 
GeneralMy vote of 5 Pin
Humayun Kabir Mamun18-Jan-15 18:53
Humayun Kabir Mamun18-Jan-15 18:53 
GeneralRe: My vote of 5 Pin
Prashant Kurlekar18-Jan-15 23:10
Prashant Kurlekar18-Jan-15 23:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.