ASP.NET MVC Partial View Validation From Client Side

I have come across one problem regarding client side validation for form submit where form resides in partial view
I have gone through many forums and articles but very few have explained it with complete example

Guess what I have done? Made one simple and small demo that I am going to explain

How unobtrusive validation works and problem associated with partial view
  • Unobtrusive validation is a unique feature. One can easily bind server side validation to client side validation provided by JQuery plugin.
  • Unobtrusive validation binds special attributes to field on load event, which are used to bind your own rules defined inside the model with rules implementation provided by JQuery library.
  • However, when you create a partial view fields in new form are without those special attributes and so validation is not working.
  • One of the solution is to load unobtrusive .js file again. It works but you need to load something twice wasting resources.
  • There is a method “parse” in unobtrusive plugin , which is responsible for creating validation
  • There is a method “parse” in unobtrusive plugin , which is responsible for creating validation attributes , adapters and also what’s really important errorPlacement adapter. Everything you need. Knowing all of this we can just use this “parse” on the form and everything should work fine.

Higher level we could divide this exercise in three sections moving forward step by step we are going to try to accomplish each section

Section 1 : Setup & Configuration

  • STEP 1 : Bundle following files in BundleConfig class BundleConfig
    • JQuery (for DOM management)
    • JQuer UI (for Dialogue box)
    • jquery.unobtrusive & jquery.validate (for validation)
    • “~/Content/css” for site.css (for site design)
    • “~/Content/themes/base/css” JQuery UI CSS (for dialogue box design)
  • STEP 2 : _layout page  _layout
    • Add STEP 1 bundles in _layout page
    • Note replace render section of script to bottom after jquery, jqueryui & jqueryval bunder
  • STEP 3 : Add appsettings in web.config  Web.config
    • set ClientValidationEnabled true
    • set UnobtrusiveJavaScriptEnabled true

#Section 1 is completed let us move forward to section 2


Section 2 : Back-end side

  • STEP 1: AddUserViewModel Model that includes following  Model
    • Properties
    • validate properties using DataAnnotations using System.ComponentModel.DataAnnotations
  • STEP 2: Create partial view incorporates followings  Partial View
    • Associated Model
    • Form elements without submit as it will be taken care of by dialogue box button
    • Validation message
  • STEP 3: HomeController has two responsibilities  HomeController
    • Render partial view (i.e. AddUserPartialView)
    • Handle submit data on form POST (i.e. AddUserInfo)

#Section 2 is completed let us move forward to section 3


Section 3 : Front end side (Index.chtml)

  • STEP 1: Add dialogue box container and adduser button Ref Code
  • STEP 2: Add button click event to open dialogue box (i.e. $(“#AddUserForm”).dialog({…}) Ref Code
  • STEP 3: Load rendered partial view in dialogue box
    • (i.e. $(‘#AddUserForm’).load(‘@Url.Action(“AddUserPartialView”, “Home”)’, function () {…}) ) Ref Code
  • STEP 4: Call “parse” in unobtrusive plugin, which is responsible for creating validation attributes Ref Code
  • STEP 5: Register form submit event on load of dialogue box (i.e. $form.submit(function () {..}) ) Ref Code
  • STEP 6: post serialized form data to /Home/AddUserInfo action by AJAX Ref Code
  • STEP 7: AJAX event has two handlers success and error Ref Code
  • STEP 8: Last but not the least add two buttons “Add User” and “Cancel” in dialogue box Ref Code
    • “Add User” call form submit
    • “Cancel” close dialog box

Source code:-
https://github.com/ganesha8shiva/MVCPartialViewValidation
Video Help :-


Note: If anybody faces error while building project

  • Right click on solution
  • Enable NuGet Package Restore (will take few minutes to finish)
  • Tools -> Library Package Manager -> Package Manager Console
  • Some NuGet Packages are missing from this solution. Click to restore.
  • Press Restore (will take several minutes)
  • Build Successful and works like charm

Video Help :-

JQuery Virtual Keyboard

A quick post on virtual keyboard which can be used to enter user name / password / Pine number, etc… Internet banking highly recommend to use while logging into Internet Banking Site.

Below are the advantages of virtual keyboard

  • The virtual keyboard can be an additional precautionary measure, protects you against malicious key logger programs which might have infected your  computer.
  • A key logger program can capture the keystrokes you enter in the physical keyboard to login which may be accessing your PC without your knowledge or permission.
  • A virtual keyboard prevents your username or password from being stolen, especially while using public computers, such as those available in Internet cafes. Also, the sequence in which the keys appear will change everytime the page is refreshed.

Before I start with an example go through Keith Wood’s solution for it build on top of JQuery

It is very clean, well documented, higly compatible and scalable solution, if I have to rate, I rate 5 out of 5 stars and highly recommended solution

Virtual Keyboard:  http://keith-wood.name/keypad.html

Quick Reference: http://keith-wood.name/keypad.html

jQuery Keypad Reference:     http://keith-wood.name/keypadRef.html

Last but not the least I am going through simple yet quick example before signing off

Step 0:- Create Folder structure as below

  • main
  • main/css
  • main/javascript

Step 1:-  Add jquery.keypad.css in main/css folder

Step 2:-  Add jquery-1.7.1.min.js and jquery.keypad.js in main/javascript folder

Step 3:-  Add index.html in main folder and copy-paste below code into index.html  “please read code carefully before copying it blindly :D”

<html>
<title>Virtual Keyboard</title>
<head>
 <link type="text/css" href="css/jquery.keypad.css" rel="stylesheet" />  
 <script type="text/javascript" src="javascript/jquery-1.7.1.min.js"></script>
 <script type="text/javascript" src="javascript/jquery.keypad.js"></script> 
    <script type="text/javascript">
        $(document).ready(function () {                    
            $("input[id$='chkKeyboard']").click(function () {            
                if (!$(this).is(':checked')) {
                    $('#txtUsername').keypad('destroy');
                    $('#txtPassword').keypad('destroy');
                } else {
                    $('#txtUsername').keypad({ keypadOnly: true, layout: $.keypad.qwertyLayout, randomiseAlphabetic: true, randomiseNumeric: true, randomiseOther: true });
                    $('#txtPassword').keypad({ keypadOnly: true, layout: $.keypad.qwertyLayout, randomiseAlphabetic: true, randomiseNumeric: true, randomiseOther: true });                    
                }
            });
        });
    </script>       

</head>

<body>
   <div style="text-align: left; margin-top: 15px;">                           
      Username : <input type="text" id="txtUsername" /><br/>
      Password : <input type="Password" id="txtPassword"></asp:TextBox><br/>
      <input type="checkbox" id="chkKeyboard" /> Use virtual keyboard (Recommended)<br/>
      <input type="button" id="btnSubmit" value="Submit"  />
   </div>
</body>

</html>

Step 4:- Open index.html in any browser (ie, chrome, firefox, etc…)
virtual_keyboard

Quote: “Great solutions are described in few words”

What is Remote Attribute?

Underline Problem:-
Any identity thing is unique example userid / emailid / username so to validate uniqueness we have to compare id with existing data
(most obvious database)

Solution:-
Decorating a model property with this attributes will instruct JQuery validate to make an HTTP request to a given action method
for server-side checking. The result is transmitted back to the client and an error message will be displayed before the form is
submitted

We are taking even number example entered at user-end
Create A View

@model MvcApplication1.Controllers.Category
<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
@{Html.EnableClientValidation(true);}
@using (Html.BeginForm("Create", "Employee", FormMethod.Post))
{
   @Html.ValidationSummary(false)
   @Html.LabelFor(m => m.PIN)
   @Html.TextBoxFor(m => m.PIN, new { maxlength=1  }) <br />        
   <input type="submit" value="Submit" />
}

Create A Model

public class Category
{
   [Remote("IsEvenNumber", "Employee", ErrorMessage = "Enter Even Number")]
   public int PIN { get; set; }
}

Create A Controller

public class EmployeeController : Controller
{
   [HttpGet]
   public ActionResult create()
   {
      return View();
   }
   public JsonResult IsEvenNumber(int PIN)
   {
      return Json(PIN % 2 == 0, JsonRequestBehavior.AllowGet);
   }
}

I can see AJAX request for validation on onkeyup for each character type in textbox
instead of tab (onBlur)

<script language="javascript" type="text/javascript">
$.validator.setDefaults({
onkeyup: false
})
</script>

What is the user of AdditionalFields attributes?

Use to send multiple values to action for validation

------------------------------------------------
Model
------------------------------------------------
public class Category
{
   [Required]
   [Remote("IsEvenNumber", "Employee", AdditionalFields = "hide_pin", ErrorMessage = "Enter Even Number & Not correct pin")]   
   public int PIN { get; set; }
}

------------------------------------------------
View
------------------------------------------------
@model MvcApplication1.Controllers.Category
<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

<script language="javascript" type="text/javascript">
    $.validator.setDefaults({
        onkeyup: false
    })
</script>

@{Html.EnableClientValidation(true);}
@using (Html.BeginForm("Create", "Employee", FormMethod.Post))
{
    @Html.ValidationSummary(false)

    @Html.LabelFor(m => m.PIN)
    @Html.TextBoxFor(m => m.PIN, new { maxlength = 4 }) <br />

    @Html.Hidden("hide_pin", "10")
    <input type="submit" value="Submit" />
}

------------------------------------------------
Controller
------------------------------------------------
public class EmployeeController : Controller
{
   [HttpGet]
   public ActionResult create()
   {
      return View();
   }

   public JsonResult IsEvenNumber(int PIN, int hide_pin)
   {
      return Json(PIN % 2 == 0 && PIN == hide_pin, JsonRequestBehavior.AllowGet);
   }
}

Behind MVC Client End Validation

Let’s straight into what things we require to enable validation at client end

STEP 1: Web.Config

Make sure below keys are required to be set true

<appSettings>
      <add key="ClientValidationEnabled" value="true"/>
      <add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>

 

STEP 2: Create One Model

using System.ComponentModel.DataAnnotations; // Required at least
public class Category
{
     [Required]
     [RegularExpression(@"^[a-z,'\s']+$", ErrorMessage = "Only A to Z with space")]
     public string Name { get; set; }

     [Required]
     [RegularExpression(@"^\d+$",ErrorMessage="Only number 0 to 9" )]
     public int Order { get; set; }
}

Note: Basically we can validate inputs by model by Annotations

 

STEP 3: Create Simple Controller

public class EmployeeController : Controller
{
     [HttpGet]
     public ActionResult create()
     {
          return View();
     }

     [HttpPost]
     public ActionResult Create(Category cat)
     {
          if (ModelState.IsValid == false)
          {
               return View(cat);
          }
          return Content("success");
     }
}

 

STEP 4: Create View

@model MvcApplication1.Controllers.Category
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@{Html.EnableClientValidation(true);}
@using (Html.BeginForm("Create", "Employee", FormMethod.Post))
{
      @Html.ValidationSummary(false)

      @Html.LabelFor(m => m.Name)
      @Html.TextBoxFor(m => m.Name) <br />

      @Html.LabelFor(m => m.Order)
      @Html.TextBoxFor(m => m.Order) <br />

      <input type="submit" value="Submit" />
}

 

Note: Please go to view source to see the magic (validation metadata)  is emitted as data attributes on the input elements.

<label for="Name">Name</label>
<input data-val="true" data-val-regex="Only A to Z with space" data-val-regex-pattern="^[a-z,'\s']+$" data-val-required="The Name field is required." id="Name" name="Name" type="text" value="" /> <br />

<label for="Order">Order</label>
<input data-val="true" data-val-number="The field Order must be a number." data-val-regex="Only number 0 to 9" data-val-regex-pattern="^\d+$" data-val-required="The Order field is required." id="Order" name="Order" type="text" value="" />

This metadata is consumed by the jquery.unobtrusive JavaScript library and connected to the jQuery Validate plugin’s validation logic.

 

Add Value:- We have added server-side validation (second gate-keeper) to make sure system won’t process invalid data.

[HttpPost]
public ActionResult Create(Category cat)
{
    if (ModelState.IsValid == false)
    {
        return View(cat);
    }
    return Content("success");
}

Because user submit data and it enters in network (un-secured world) where it can be tempered before it is reached to server

*****
Otherwise no need of SSL 😀 By The Way It provides tunnel from client to server using asymmetric cryptography so client data(soldiers) can reach destination and no-body can see what’s transferring.
*****

MCV Fill DropDownBox !!!

Let’s start with an example how to fill dropdownbox with
a very very simple example of (Employee Grade List) as I believe

– “Solution can be described in few words”

# Fill Dropdownbox
Key Note: We need SelectListItem class of System.Web.Mvc

Employee Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplication1.Models;
namespace MvcApplication1.Controllers
{
     public class EmployeeController : Controller
     {
          public ActionResult index()
          {
               var Grade = new List();
               Grade.Add(new SelectListItem() { Text = "A", Value = "1" });
               Grade.Add(new SelectListItem() { Text = "B", Value = "2" });
               Grade.Add(new SelectListItem() { Text = "C", Value = "3" });
               Grade.Add(new SelectListItem() { Text = "D", Value = "4" });
               Grade.Add(new SelectListItem() { Text = "E", Value = "5" });
               Grade.Add(new SelectListItem() { Text = "F", Value = "6" });
               ViewBag.Grade = Grade;
               return View();
          }
     }
}

Employee Index View (index.cshtml):
Employee<br/>
Grade: @Html.DropDownList("Grade")

Step1

# Set Default Text (Note: Just add default text for optionLable)

Employee<br/>
Grade: @Html.DropDownList("Grade","Select Grade")

Step2

# Select value for edit before update (Note: Just add selected true for SelectListItem)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplication1.Models;
namespace MvcApplication1.Controllers
{
   public class EmployeeController : Controller
   {
     public ActionResult index()
     {
       var Grade = new List<SelectListItem>();
       Grade.Add(new SelectListItem() { Text = "A", Value = "1" });
       Grade.Add(new SelectListItem() { Text = "B", Value = "2" });
       Grade.Add(new SelectListItem() { Text = "C", Value = "3" });
       Grade.Add(new SelectListItem() { Text = "D", Value = "4" });
       Grade.Add(new SelectListItem() { Text = "E", Value = "5", Selected=true });  // This option will be selected by default
       Grade.Add(new SelectListItem() { Text = "F", Value = "6" });
       ViewBag.Grade = Grade;
       return View();
     }
   }
}

Step3

Note: Grade system creates division among people !!!

Explore Microsoft MVC Environment Variables

We start MVC3 example with exploring/listing Environment Variables contains application sessionid, cockie, configuration, browser, etc… details

We try to finish this quest only by Controller without including unnecessary cosmetic things (view :D)

Create Controller: QueryStringController

public class QueryStringController : Controller
{
  public ActionResult Index()
  {
    Parameters();
    return Content("Index page is called");
  }

  [NonAction]
  public void Parameters()
  {
     foreach (string key in Request.Params.Keys)
     {
       Response.Write(string.Format("<strong>{0}:</strong> {1}<br/>", key, Request[key]));
      }
  }
}

HIT: http://localhost/querystring/index

———————————————————————————————————————————

We can make this above controller even shorter remove [NonAction], use Parameters methode as Action

public class QueryStringController : Controller
{
  public void Parameters()
  {
    foreach (string key in Request.Params.Keys)
    {
      Response.Write(string.Format("<strong>{0}:</strong> {1}<br/>", key, Request[key]));
    }
  }
}

HIT: http://localhost/querystring/Parameters

Environment Variables

__atuvc: 9%7C52%2C2%7C1
Nop.customer: 51ceb03d-b130-49ce-a0af-640f745c8e14
ASP.NET_SessionId: 5emy123x0tn2tnzvlev3arrc
ALL_HTTP: HTTP_CACHE_CONTROL:no-cache HTTP_CONNECTION:keep-alive HTTP_PRAGMA:no-cache HTTP_ACCEPT:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 HTTP_ACCEPT_CHARSET:ISO-8859-1,utf-8;q=0.7,*;q=0.3 HTTP_ACCEPT_ENCODING:gzip,deflate,sdch HTTP_ACCEPT_LANGUAGE:en-US,en;q=0.8 HTTP_COOKIE:__atuvc=9%7C52%2C2%7C1; Nop.customer=51ceb03d-b130-49ce-a0af-640f745c8e14; ASP.NET_SessionId=5emy123x0tn2tnzvlev3arrc HTTP_HOST:localhost:58550 HTTP_USER_AGENT:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17
ALL_RAW: Cache-Control: no-cache Connection: keep-alive Pragma: no-cache Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8 Cookie: __atuvc=9%7C52%2C2%7C1; Nop.customer=51ceb03d-b130-49ce-a0af-640f745c8e14; ASP.NET_SessionId=5emy123x0tn2tnzvlev3arrc Host: localhost:58550 User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17
APPL_MD_PATH:
APPL_PHYSICAL_PATH: C:\bhumip_pro\EF4MVC\MvcApplication1\
AUTH_TYPE:
AUTH_USER:
AUTH_PASSWORD:
LOGON_USER: VIBHUTI-PC2\vibhuti
REMOTE_USER:
CERT_COOKIE:
CERT_FLAGS:
CERT_ISSUER:
CERT_KEYSIZE:
CERT_SECRETKEYSIZE:
CERT_SERIALNUMBER:
CERT_SERVER_ISSUER:
CERT_SERVER_SUBJECT:
CERT_SUBJECT:
CONTENT_LENGTH: 0
CONTENT_TYPE:
GATEWAY_INTERFACE:
HTTPS:
HTTPS_KEYSIZE:
HTTPS_SECRETKEYSIZE:
HTTPS_SERVER_ISSUER:
HTTPS_SERVER_SUBJECT:
INSTANCE_ID:
INSTANCE_META_PATH:
LOCAL_ADDR: 127.0.0.1
PATH_INFO: /querystring/index
PATH_TRANSLATED: C:\bhumip_pro\EF4MVC\MvcApplication1\querystring\index
QUERY_STRING:
REMOTE_ADDR: 127.0.0.1
REMOTE_HOST: 127.0.0.1
REMOTE_PORT:
REQUEST_METHOD: GET
SCRIPT_NAME: /querystring/index
SERVER_NAME: localhost
SERVER_PORT: 58550
SERVER_PORT_SECURE: 0
SERVER_PROTOCOL: HTTP/1.1
SERVER_SOFTWARE:
URL: /querystring/index
HTTP_CACHE_CONTROL: no-cache
HTTP_CONNECTION: keep-alive
HTTP_PRAGMA: no-cache
HTTP_ACCEPT: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
HTTP_ACCEPT_CHARSET: ISO-8859-1,utf-8;q=0.7,*;q=0.3
HTTP_ACCEPT_ENCODING: gzip,deflate,sdch
HTTP_ACCEPT_LANGUAGE: en-US,en;q=0.8
HTTP_COOKIE: __atuvc=9%7C52%2C2%7C1; Nop.customer=51ceb03d-b130-49ce-a0af-640f745c8e14; ASP.NET_SessionId=5emy123x0tn2tnzvlev3arrc
HTTP_HOST: localhost:58550
HTTP_USER_AGENT: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17