You will first need to start with a view model to represent your dates
public class DateVM
{
public DateVM() { } // default constructor required by the DefaultModelBinder
public DateVM(DateTime date) // useful if your editing existing dates
{
Date date.Date;
Day = date.Day;
Month = date.Month;
Year = date.Year;
}
public int Day { get; set; }
public int Month { get; set; }
public int Year { get; set; }
[Required(ErrorMessage = "The values for the date are not valid")]
public DateTime Date { get; set; }
}
and then the main view model would contain a public List<DateVM> Dates {get; set; }
property.
While it would be possible to create a custom ValidationAttribute
that implements IClientValidatable
, (refer The Complete Guide to Validation-in-ASP.NET-MVC-3 Part-2 for a good guide to implementing one) it presents a few design problems such as which property do you apply the attribute to?, what happens when one of the other properties is not edited? etc.
Instead, I recommend you just include your own scripts to handle the .change()
events of each control and validate the date (and display an error message if appropriate), and also handle the forms .submit()
event to cancel the post if invalid.
Your part of the view for editing the dates would need to be
for(int i = 0; i < Model.Dates.Count; i++)
{
<div class="date-container">
@Html.LabelFor(m => m.Dates[i].Year)
@Html.TextBoxFor(m => m.Dates[i].Year, new { @class = "year" })
@Html.ValidationMessageFor(m => m.Dates[i].Year)
@Html.LabelFor(m => m.Dates[i].Month)
@Html.TextBoxFor(m => m.Dates[i].Month, new { @class = "month" })
@Html.ValidationMessageFor(m => m.Dates[i].Month)
@Html.LabelFor(m => m.Dates[i].Day)
@Html.TextBoxFor(m => m.Dates[i].Day, new { @class = "day" })
@Html.ValidationMessageFor(m => m.Dates[i].Day)
@Html.HiddenFor(m => m.Dates[i].Date, new { @class = "date" })
@Html.ValidationMessageFor(m => m.Dates[i].Date, null, new { @class = "validation" })
</div>
}
and then include the following scripts
$('.day, .month, .year').change(function () {
var container = $(this).closest('.date-container');
var day = Number(container.find('.day').val());
var month = Number(container.find('.month').val());
var year = Number(container.find('.year').val());
if (isNaN(day) || isNaN(month) || isNaN(year) || day === 0 || month === 0 || year === 0) {
container.find('.date').val('');
return;
}
var date = new Date(year, month - 1, day);
var isValid = date.getDate() === day && date.getMonth() === month - 1 && date.getFullYear() === year;
if (isValid) {
container.find('.date').val(year + '-' + month + '-' + day);
container.find('.validation').text('').addClass('field-validation-valid').removeClass('field-validation-error');
} else {
container.find('.date').val('');
container.find('.validation').text('The values for the date are not valid').addClass('field-validation-error').removeClass('field-validation-valid');
}
});
$('form').submit(function () {
$.each($('.date'), function () {
if (!$(this).val()) {
return false;
}
});
})