Пользовательская проверка MVC: сравните две даты
Я создал собственный атрибут ValidationAttribute, который сравнивает 2 даты и гарантирует, что вторая дата больше первой:
public sealed class IsDateAfter : ValidationAttribute, IClientValidatable
{
private readonly string testedPropertyName;
private readonly bool allowEqualDates;
public IsDateAfter(string testedPropertyName, bool allowEqualDates = false)
{
this.testedPropertyName = testedPropertyName;
this.allowEqualDates = allowEqualDates;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var propertyTestedInfo = validationContext.ObjectType.GetProperty(this.testedPropertyName);
if (propertyTestedInfo == null)
{
return new ValidationResult(string.Format("unknown property {0}", this.testedPropertyName));
}
var propertyTestedValue = propertyTestedInfo.GetValue(validationContext.ObjectInstance, null);
if (value == null || !(value is DateTime))
{
return ValidationResult.Success;
}
if (propertyTestedValue == null || !(propertyTestedValue is DateTime))
{
return ValidationResult.Success;
}
// Compare values
if ((DateTime)value >= (DateTime)propertyTestedValue)
{
if (this.allowEqualDates)
{
return ValidationResult.Success;
}
if ((DateTime)value > (DateTime)propertyTestedValue)
{
return ValidationResult.Success;
}
}
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = this.ErrorMessageString,
ValidationType = "isdateafter"
};
rule.ValidationParameters["propertytested"] = this.testedPropertyName;
rule.ValidationParameters["allowequaldates"] = this.allowEqualDates;
yield return rule;
}
CalendarEntry класс: ...
public virtual DateTime StartDate { get; set; }
[IsDateAfter("StartDate", true, ErrorMessage="End date needs to be after start date")]
public virtual DateTime EndDate { get; set; }
ПОСМОТРЕТЬ:
$.validator.unobtrusive.adapters.add(
'isdateafter', ['propertytested', 'allowequaldates'], function (options) {
options.rules['isdateafter'] = options.params;
options.messages['isdateafter'] = options.message;
});
$.validator.addMethod("isdateafter", function(value, element, params) {
alert(params.propertytested);
var startdatevalue = $('input[name="' + params.propertytested + '"]').val();
if (!value || !startdatevalue) return true;
return (params.allowequaldates) ? Date.parse(startdatevalue) <= Date.parse(value) : Date.parse(startdatevalue) < Date.parse(value);
}, '');
Это прекрасно работает, когда CalendarEntry не заключен в другой класс. ОДНАКО, когда я использую модель вида примерно так:
public class TrainingDateEditViewModel
{
#region Properties
/// <summary>
/// Gets or sets CalendarEntry.
/// </summary>
public CalendarEntry CalendarEntry { get; set; }
....
Проверка клиента больше не работает, потому что вывод html выглядит так:
<input type="text" value="" name="CalendarEntry.EndDate" id="CalendarEntry_EndDate" data-val-isdateafter-propertytested="StartDate" data-val-isdateafter-allowequaldates="True" data-val-isdateafter="End date needs to be after start date" data-val="true">
И
data-val-isdateafter-propertytested="StartDate" and IT SHOULD BE: "CalendarEntry.StartDate".
Как бы я сделал так, чтобы он знал, чтобы связать с "CalendarEntry.StartDate" rule.ValidationParameters["propertytested"] = this.testedPropertyName; // ЗДЕСЬ ДОЛЖЕН БЫТЬ ФИО??? КАК??
Спасибо
4 ответа
Вам нужно изменить свой клиентский скрипт, чтобы проверить префикс тестируемого элемента, и добавить префикс (если есть) к вашему селектору следующим образом:
$.validator.addMethod("isdateafter", function(value, element, params) {
var parts = element.name.split(".");
var prefix = "";
if (parts.length > 1)
prefix = parts[0] + ".";
var startdatevalue = $('input[name="' + prefix + params.propertytested + '"]').val();
if (!value || !startdatevalue)
return true;
return (params.allowequaldates) ? Date.parse(startdatevalue) <= Date.parse(value) :
Date.parse(startdatevalue) < Date.parse(value);
});
Не забудьте включить клиентскую часть в этот код. У меня ушло много часов, чтобы обнаружить, что этого не хватает!
(function ($) {
// your code here..
})(jQuery);
Просто чтобы исправить небольшую ошибку в javascript Counsellorben: "(params.allowequaldates)" будет интерпретироваться как строка (которая будет иметь значение "False" или "True"), но эта строка всегда будет оцениваться как true, таким образом, всегда разрешая равные даты. Если вы также хотите разрешить больше уровней вложенности ваших объектов, чем просто 1, то вы получите:
$.validator.addMethod("isdateafter", function(value, element, params) {
var parts = element.name.split(".");
var prefix = "";
for (var i = 0; i < parts.length - 1; i++)
prefix = parts[i] + ".";
var startdatevalue = $('input[name="' + prefix + params.propertytested + '"]').val();
if (!value || !startdatevalue)
return true;
var allowequal = params.allowequaldates.toLowerCase === "true";
return allowequal ? Date.parse(startdatevalue) <= Date.parse(value) :
Date.parse(startdatevalue) < Date.parse(value);
});
В последнем ответе были некоторые пропущенные скобки при вызове toLowerCase, вот обновленная версия с готовым документом и частью $.validator.unobtrusive...-:
$(function () {
$.validator.addMethod("isdateafter", function(value, element, params) {
var parts = element.name.split(".");
var prefix = "";
for (var i = 0; i < parts.length - 1; i++) {
prefix = parts[i] + ".";
}
var startdatevalue = $('input[name="' + prefix + params.propertytested + '"]').val();
if (!value || !startdatevalue) return true;
var allowequal = params.allowequaldates.toLowerCase() === "true";
return allowequal ? Date.parse(startdatevalue) <= Date.parse(value) :
Date.parse(startdatevalue) < Date.parse(value);
});
$.validator.unobtrusive.adapters.add('isdateafter',
['propertytested', 'allowequaldates'],
function (options) {
options.rules['isdateafter'] = options.params;
options.messages['isdateafter'] = options.message;
});
});