One of the many nice things they have added in MVC 2 is model/view model validation on both the client and server side. I won’t go into the basics here since they have been covered extensively by others. However, recently I was adding validation to require a value for a hidden field on a form, and found that the default validation functionality just didn’t work on the client side for a hidden field. Here I will go over how I added some custom validation to allow me to do it.
While it might sound weird to validate a hidden field, something a user will never directly interact with, in this age of complex interfaces built in JavaScript it’s easy to imagine a scenario where a series of interactions leads to storing some kind of value in a hidden field. If that end value is something that is required, it’s only natural that that should be enforced by the model.
First, let’s start by defining a very simple view model for the form:
public class DemoForm { [Required(ErrorMessage = "You must enter a name")] public string Name { get; set; } }
It just defines a single property for storing a name. Now we can define a very basic view that is strictly typed to that view model. Here is the code for setting up the form:
<% using (Html.BeginForm()) { %> <%= Html.HiddenFor(model => model.Name) %> <p> <%= Html.ValidationMessageFor(model => model.Name) %> </p> <input type="submit" value="Submit" /> <% } %>
For the sake of this demo, I am going to add some buttons that can populate and clear the hidden field. To get that out of the way, here is the markup for that:
<div> <input id="PopulateField" type="button" value="Populate Hidden Field" /> <input id="ClearField" type="button" value="Clear Hidden Field" /> </div> <p> Last submitted value: <%= ViewData["Name"] %> </p>
and here is some JavaScript (using jQuery) to wire them up:
And now we’re almost done with the boring part. Finally, we wire up the controller actions like so:
[HttpGet] public ActionResult Index() { return View(); } [HttpPost] public ActionResult Index(DemoForm form) { ViewData["Name"] = form.Name; return View(form); }
If you run the page now, you will get correct server-side validation, but nothing on the client. To get client validation in MVC, we need to tell the page to enable client validation. To do that, call this before starting the form:
<% Html.EnableClientValidation(); %>
You will also need some JavaScript references to these files, which are included by default in any new MVC project:
<script type="text/javascript" src="<%= ResolveUrl("~/Scripts/MicrosoftAjax.js") %>"></script> <script type="text/javascript" src="<%= ResolveUrl("~/Scripts/MicrosoftMvcAjax.js") %>"></script> <script type="text/javascript" src="<%= ResolveUrl("~/Scripts/MicrosoftMvcValidation.js") %>"></script>
Now if you reload the page, client validation still isn’t working. Why not? After digging through the Microsoft validation scripts a bit, I found that the Required validator ignores hidden fields altogether in JavaScript. We could change it from being a hidden field to a text field and the same code would work as expected, but that’s not what we want to do.
As it turns out, they made it rather simple to extend the built-in validation with your own custom rules. First, we’ll define the attribute class, so that it can be added as a decorator to a property the same way the other validators work. In this case, since the Required attribute actually works fine on the server side, our class will extend that and do nothing else. This way we can provide the client side functionality and rely on the existing server validation.
public class HiddenRequiredAttribute : RequiredAttribute { }
Now that we have the attribute, we need a validator that uses it. To do that, we extend the DataAnnotationsModelValidator class, and tell it to use the attribute we just defined:
public class HiddenRequiredValidator : DataAnnotationsModelValidator<HiddenRequiredAttribute> { private string _errorMessage; public HiddenRequiredValidator(ModelMetadata metaData, ControllerContext context, HiddenRequiredAttribute attribute) : base(metaData, context, attribute) { _errorMessage = attribute.ErrorMessage; } public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() { var rule = new ModelClientValidationRule { ErrorMessage = _errorMessage, ValidationType = "hiddenRequired" }; return new[] { rule }; } }
The important part to notice here is where we set the ValidationType to “hiddenRequired.” This is the unique identifier we define for referencing our validator in JavaScript. Remember it, because you’re going to need it later.
So now we have an attribute, and a validator for it. Next up, we need to register our validator in the application’s Application_Start function:
DataAnnotationsModelValidatorProvider .RegisterAdapter(typeof(HiddenRequiredAttribute), typeof(HiddenRequiredValidator));
Now let’s switch the view model’s Name property to use the new HiddenRequired attribute instead of the Required one we had before:
public class DemoForm { [HiddenRequired(ErrorMessage = "You must enter a name")] public string Name { get; set; } }
All that remains is to put in some JavaScript code to handle the validation. I’ll show the code here, and then discuss it. Since this is a contained demo, I will add the code directly to the view.
Sys.Mvc.ValidatorRegistry.validators["hiddenRequired"] = function(rule) { return function(value, context) { if (value) { return true; } return rule.ErrorMessage; } };
If that code looks simple, that’s because it is. Since we’re only validating that the field has some kind of value, that is all we need to check for. The reason that the return function is wrapped in another function is to allow for adding more complex logic before the return function if needed. For this example, it isn’t needed.
Now if you reload the page you will get the client-side validation we were aiming for, triggered by the submit button. Always remember that client-side validation is just there for improving the user experience, and is no substitute for server validation.
.............................................................................................................................................................
In the previous posts in this series I introduced the client side state managementand one of its techniques - the ViewState. Today I'm going to drill down into the hidden fields technique. As mentioned in the previous post, the ViewState stores its state in a hidden field.
What are Hidden Fields?
Hidden fields technique is widely used in ASP.NET programing. Hidden fields are html input control with hidden type that store hidden data in the html. An example for a hidden field can look like this:
1.
<
input
type
=
"hidden"
name
=
"__EVENTTARGET"
id
=
"__EVENTTARGET"
value
=
""
/>
In the example above, I chose to show the event target hidden field in order to indicate that even in postback mechanism hidden fields are being used. The data stored in a hidden field is available when the form is processed on the server or when we use javascript.
Hidden Fields Values
Hidden fields store only one value in their value property. The value is saved as a string and therefore in order to use it for other types you need to perform casting. Hidden fields' data is submitted to the server only in HTTP post operation. You can see the stored data easily by using the View Source operation of the browser. You can see it by clicking the right mouse button and then choosing View Source from the menu (if the operation is available). Be aware not to use hidden fields to store confidential data! The values has page context and therefore when you leave a page the data stored in the hidden fields is disposed.
Server Control Hidden Fields
There are two types of server control hidden fields - System.Web.UI.WebControls.HiddenField and System.Web.UI.HtmlControls.HtmlInputHidden. Both types has the same primary Value property to hold the value of the hidden field. You should choose between these types whenever you need to use a server side hidden field (A note - The difference between HtmlControls and WebControls isn't in the context of this post). When you don't use server controls you can use the Request.Form NameValueCollection to get the hidden field value by providing the client
id of the hidden field. For example the code bellow will return the string value of the __EVENTTARGET hidden field:
id of the hidden field. For example the code bellow will return the string value of the __EVENTTARGET hidden field:
1.
string
eventTarget = Request.Form[
"__EVENTTARGET"
];
Hidden Fields Example
I got some requests for an example of how to use the hidden fields inside web forms. The example is very easy to understand. I have an html input with type hidden. In the first request I fill the input with a message value with javascript function. I use a button control to do a post back to the server and in the post back the value of the hidden
field is inserted into a label control. Pay attention that because I use an html hidden field that isn’t a server control after a second post back it’s value will be empty. The only reason that the label will still show a message is because
the label’s default value of the EnableViewState is true. The web page’s code:
field is inserted into a label control. Pay attention that because I use an html hidden field that isn’t a server control after a second post back it’s value will be empty. The only reason that the label will still show a message is because
the label’s default value of the EnableViewState is true. The web page’s code:
01.
<%@ Page="" Language="C#" AutoEventWireup="true" Codebehind="Default.aspx.cs"
02.
Inherits="AJAXEnabledWebApplication1._Default" EnableSessionState="False" %>
03.
04.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
05.
06.
<
html
xmlns
=
"http://www.w3.org/1999/xhtml"
>
07.
08.
<
head
runat
=
"server"
>
09.
<
title
>Untitled Page</
title
>
10.
11.
<
script
type
=
"text/javascript"
>
12.
function PutHtmlHiddenFieldValue(message)
13.
{
14.
var hidden = document.getElementById('HTMLHiddenField');
15.
16.
if (hidden != null)
17.
{
18.
hidden.value = message;
19.
}
20.
}
21.
</
script
>
22.
</
head
>
23.
24.
<
body
dir
=
"ltr"
>
25.
<
form
id
=
"form1"
runat
=
"server"
>
26.
<
div
>
27.
<
div
>
28.
<
asp:Label
ID
=
"lblHTMLHiddenField"
runat
=
"server"
></
asp:Label
>
29.
<
input
id
=
"HTMLHiddenField"
type
=
"hidden"
name
=
"HTMLHiddenField"
/>
30.
</
div
>
31.
32.
<
div
>
33.
<
asp:Button
ID
=
"btnForPostBack"
runat
=
"server"
Text
=
"Do Postback"
/>
34.
</
div
>
35.
</
div
>
36.
</
form
>
37.
</
body
>
38.
</
html
>
On the server side:
01.
public
partial
class
_Default : System.Web.UI.Page
02.
{
03.
#region Consts
04.
05.
private
const
string
SCRIPT_KEY =
"HtmlHiddenFieldScript"
;
06.
07.
#endregion
08.
09.
#region Page Events
10.
11.
protected
void
Page_Load(
object
sender, EventArgs e)
12.
{
13.
14.
// Insert message to the label of the html control hidden
15.
// field if there is a value in the html hidden field
16.
17.
string
message = Request.Form[
"HTMLHiddenField"
];
18.
19.
if
(!
string
.IsNullOrEmpty(message))
20.
{
21.
lblHTMLHiddenField.Text = message;
22.
}
23.
}
24.
25.
protected
void
Page_PreRender(
object
sender, EventArgs e)
26.
{
27.
28.
// Register a startup script in order to fill the html
29.
// hidden field with a message value
30.
31.
if
(!ClientScript.IsClientScriptBlockRegistered(
"HtmlHiddenFieldScript"
) && !IsPostBack)
32.
{
33.
34.
ClientScript.RegisterStartupScript(
typeof
(Page), SCRIPT_KEY,
"PutHtmlHiddenFieldValue('Html hidden hello world');"
,
true
);
35.
36.
}
37.
}
38.
39.
#endregion
40.
}
Summary
To sum up the post, today I drilled down into the hidden fields technique. This technique is very popular and is used widely in ASP.NET. In the next post I'll continue the tour in the client side state management techniques.
0 comments:
Post a Comment