I have a model to validate and the problem is the date of birth field. It must be composed of 3 dropdowns (day, month, year).
<div id="dob-editor-field" class="model-field-editor">
@Html.LabelFor(m => m.DateOfBirth, new { @class = "label-div" })
@Html.Telerik().DropDownList().Name("DobDay").BindTo((SelectList)ViewData["Days"]).HtmlAttributes(new {id = "DobDaySel"})
@Html.Telerik().DropDownList().Name("DobMonth").BindTo((SelectList)ViewData["Months"]).HtmlAttributes(new { id = "DobMonthSel"})
@Html.Telerik().DropDownList().Name("DobYear").BindTo((SelectList)ViewData["Years"]).HtmlAttributes(new { id = "DobYearSel" })
@Html.ValidationMessageFor(m => m.DateOfBirth)
</div>
On the server side i do this
[HttpPost]
public ActionResult Register(RegistrationModel regInfo, int DobDay, int DobMonth, int DobYear)
{
SetRegisterViewData(DobDay, DobMonth, DobYear);
if (DobDay == 0 || DobMonth == 0 && DobYear == 0)
{
ModelState.AddModelError("DateOfBirth", "Date of birth is required");
}
else
{
DateTime dt = new DateTime(DobYear, DobMonth, DobDay);
long ticks = DateTime.Now.Ticks - dt.Ticks;
int years = new DateTime(ticks).Year;
if (years < 18)
{
ModelState.AddModelError("DateOfBirth", "You must be at least 18");
}
}
if (ModelState.IsValid)
{
//register user
return RedirectToAction("Index", "Home");
}
return View(regInfo);
}
Questions:
- Server side : how to make it better? (i am thinking of adding dob, month, and year properties RegistrationModel and add attribute on DateOfBirth to check those properties)
- Client side : i was looking at Perform client side validation for custom attribute but it got me confused. What is the way to make it?
LE: I created a custom model binder for the date like this:
public class DobModelBinder : DefaultModelBinder
{
protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
{
if (propertyDescriptor.Name == "DateOfBirth")
{
DateTime dob = DateTime.MinValue;
var form = controllerContext.HttpContext.Request.Form;
int day = Convert.ToInt32(form["DobDay"]);
int month = Convert.ToInt32(form["DobMonth"]);
int year = Convert.ToInt32(form["DobYear"]);
if (day == 0 || month == 0 || year == 0)
{
SetProperty(controllerContext, bindingContext, propertyDescriptor, DateTime.MinValue);
}
else
{
SetProperty(controllerContext, bindingContext, propertyDescriptor, new DateTime(year, month, day));
}
}
else
{
base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
}
}
}
I registered it like this:
ModelBinders.Binders.Add(typeof(DateTime), new DobModelBinder());
I used it like this:
public ActionResult Register([ModelBinder(typeof(DobModelBinder))]RegistrationModel regInfo)
DateOfBirth binds well.
LE2:
I created validation attributes for the date of birth like this:
public override bool IsValid(object value)
{
DateTime date = Convert.ToDateTime(value);
return date != DateTime.MinValue;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationRule
{
ErrorMessage = this.ErrorMessage,
ValidationType = "dateRequired"
};
}
}
public class DateGraterThanEighteen : ValidationAttribute, IClientValidatable
{
public override bool IsValid(object value)
{
DateTime date = Convert.ToDateTime(value);
long ticks = DateTime.Now.Ticks - date.Ticks;
int years = new DateTime(ticks).Year;
return years >= 18;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationRule
{
ErrorMessage = this.ErrorMessage,
ValidationType = "dateGraterThanEighteen"
};
}
}
I applied attributes like this
[DateGraterThanEighteen(ErrorMessage="You must be at least 18")]
[DateRequired(ErrorMessage = "Date of birth is required")]
public DateTime DateOfBirth { get; set; }
LE3:
In the client side i do this:
$(function () {
jQuery.validator.addMethod('dobRequired', function (value, element, params) {
if (!/Invalid|NaN/.test(new Date(value))) {
return true;
}
else {
return false;
}
}, '');
jQuery.validator.unobtrusive.adapters.add('dateRequired', {}, function (options) {
options.rules['dobRequired'] = true;
options.messages['dobRequired'] = options.message;
});
});
Client validation doesn't seems to work. How can I fix it? I am kinda confused with the way these adapters work.