I just created a sample MVC3 application to study validation. It uses DataAnnotations. I created a custom ValidationAttribute named CustomStartLetterMatch. It implements "System.Web.Mvc.IClientValidatable". I have the appropriate client-side code written with unobtrusive jQuery. This works as expected.
About the user validator: it compares the input of the name and the name of the first name. It throws an error if the first character of both of them is not the same.
As I said, the application works fine. But when I looked at rule.ValidationType = "greaterdate";, I was embarrassed. I wanted to change it to something like "anotherDefaultType". When I change it, it fails with jQuery error.
- What is the reason for this?
- What are the valid ValidationType templates?
- What is the suggested approach for changing ValidationType in this scenario.
CODE:
using System;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
namespace MyValidationTEST
{
public class Person
{
[Required(ErrorMessage = "First name required")]
public string FirstName { get; set; }
[CustomStartLetterMatch("FirstName")]
[StringLength(5,ErrorMessage = "Must be under 5 characters")]
public string LastName { get; set; }
[Range(18,50,ErrorMessage="Must be between 18 and 50")]
public int Age { get; set; }
}
public sealed class CustomStartLetterMatch : ValidationAttribute, System.Web.Mvc.IClientValidatable
{
private const string _defaultErrorMessage = " First letter of '{0}' must be same as first letetr of '{1}'";
private string _basePropertyName;
public CustomStartLetterMatch(string basePropertyName)
: base(_defaultErrorMessage)
{
_basePropertyName = basePropertyName;
}
public override string FormatErrorMessage(string name)
{
return string.Format(_defaultErrorMessage, name, _basePropertyName);
}
protected override ValidationResult IsValid(object value, System.ComponentModel.DataAnnotations.ValidationContext validationContext)
{
var basePropertyInfo = validationContext.ObjectType.GetProperty(_basePropertyName);
var baseValue = (string)basePropertyInfo.GetValue(validationContext.ObjectInstance, null);
var currentValue = (string)value;
string firstLetterBaseValue = baseValue.Substring(0, 1);
string firstLetterCurrentValue = currentValue.Substring(0, 1);
if (!string.Equals(firstLetterBaseValue, firstLetterCurrentValue))
{
var message = FormatErrorMessage(validationContext.DisplayName);
return new ValidationResult(message);
}
return null;
}
public IEnumerable<System.Web.Mvc.ModelClientValidationRule> GetClientValidationRules(System.Web.Mvc.ModelMetadata metadata, System.Web.Mvc.ControllerContext context)
{
var rule = new System.Web.Mvc.ModelClientValidationRule();
rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
rule.ValidationParameters.Add("other", _basePropertyName);
rule.ValidationType = "greaterdate";
yield return rule;
}
}
}
VIEW
@model MyValidationTEST.Person
@{
ViewBag.Title = "Create";
}
<h2>Create</h2>
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"> </script>
@*UnObtrusive*@
<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 type="text/javascript">
jQuery.validator.unobtrusive.adapters.addSingleVal("greaterdate", "other");
jQuery.validator.addMethod("greaterdate",
function (val, element, other)
{
var modelPrefix = element.name.substr(0, element.name.lastIndexOf(".") + 1)
var otherVal = $("[name=" + modelPrefix + other + "]").val();
if (val && otherVal)
{
var lastNameFirstLetter = val.substr(0, 1);
var firstNameFirstLetter = otherVal.substr(0, 1);
if (lastNameFirstLetter != firstNameFirstLetter)
{
return false;
}
}
return true;
});
</script>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Person</legend>
<div class="editor-label">
@Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.FirstName)
@Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.LastName)
@Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Age)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Age)
@Html.ValidationMessageFor(model => model.Age)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
CONTROLLER:
using System.Web.Mvc;
namespace MyValidationTEST.Controllers
{
public class RelativesController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Create()
{
Person person = new Person();
return View(person);
}
[HttpPost]
public ActionResult Create(Person relativeToAdd)
{
if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
return View(relativeToAdd);
}
}
}
READING:
ASP.NET MVC3 - Custom Validation Attribute -> Client Side Does Not Work