Sometimes it is required that some properties in a MVC model should not be validated, but the rest are required and should be validated in the client view. Mostly I cannot turn off client validation for all properties in the model.
Here is an example of a Model class. I need to validate 2 properties and the other 2 should not be validated. (client side).
using System; namespace Mvc4DateTimeClientValidationTest.Models { public class TestModel { public DateTime Date1 { get; set; } public DateTime? Date2 { get; set; } public DateTime NoValidationDate1 { get; set; } public DateTime? NoValidationDate2 { get; set; } } }
To achieve this, the HtmlHelper class is extended.
using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Web.Routing; using System.Web.Mvc; namespace Mvc4DateTimeClientValidationTest { public static class HtmlHelperExtensions { public static MvcHtmlString HiddenForWithoutClientValidation<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) { return HiddenForWithoutClientValidation(htmlHelper, expression, null); } public static MvcHtmlString HiddenForWithoutClientValidation<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) { var name = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(expression)); var id = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(ExpressionHelper.GetExpressionText(expression)); var value = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model; var routeValueDictionary = htmlAttributes != null ? new RouteValueDictionary(htmlAttributes) : new RouteValueDictionary(); return HiddenForWithoutClientValidation(name, id, value, routeValueDictionary); } private static MvcHtmlString HiddenForWithoutClientValidation(string name, string id, object value, IEnumerable<KeyValuePair<string, object>> routeValueDict) { var input = new TagBuilder("input"); if (!string.IsNullOrEmpty(id)) { input.Attributes.Add("id", id); } input.Attributes.Add("name", name); input.Attributes.Add("type", "hidden"); foreach (var routeValue in routeValueDict) { input.MergeAttribute(routeValue.Key, routeValue.Value.ToString(), true); } if (value != null) { input.Attributes.Add("value", value.ToString()); } return new MvcHtmlString(input.ToString(TagRenderMode.SelfClosing)); } } }
No changes are required for the controller class, unless I validate the model server side
public class HomeController : Controller { public ActionResult Index() { var t = new TestModel { Date1 = DateTime.Now, Date2 = DateTime.Now, NoValidationDate1 = DateTime.Now, NoValidationDate2 = DateTime.Now }; return View(t); } }
And here’s how the new extension is used in the Razor view: (@Html.HiddenForWithoutClientValidation(m => m.NoValidationDate1))
@using System.Web.Optimization @using Mvc4DateTimeClientValidationTest @model Mvc4DateTimeClientValidationTest.Models.TestModel <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="de-DE"> <head> <title>title</title> @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/jqueryui") @Scripts.Render("~/bundles/jqueryval") @Styles.Render("~/Content/css") @Styles.Render("~/Content/themes/base/css") </head> <body> <div> @Html.HiddenFor(m => m.Date1) @Html.HiddenFor(m => m.Date2) @Html.HiddenForWithoutClientValidation(m => m.NoValidationDate1) @Html.HiddenForWithoutClientValidation(m => m.NoValidationDate2) </div> </body> </html>
Using Firebug, the result can be viewed. You can see the difference in the html input controls. 2 input controls will be validated client side and 2 will not be validated.
This could also be achieved in the view using javascript.
Code: https://github.com/damienbod/Mvc4DateTimeClientValidationTest