创建具有不显眼验证的仅时间输入字段时遇到一些问题,映射到不可为空的日期时间字段。
我正在使用数据库优先的方法。 数据库具有用于存储日期和时间的日期时间字段。 我的意思是有些日期时间有真实的日期数据和 00:00:00 的时间,而另一些则有无意义的实时日期数据。 我知道这是由于 EF5 和 C# 数据类型的限制造成的。 现在使用 EF6,但不打算更改数据库。
这是视图模型
[UIHint("Date")]
public DateTime MatchDate { get; set; }
[UIHint("Time")]
public DateTime StartTime { get; set; }
仅日期字段"匹配日期"正在工作。 它使用Date.cshtml
(下图(的 EditorFor 模板。 它允许文本输入,正确附加jQuery日期选择器,并以不显眼的方式正确验证服务器端和客户端。
@model DateTime
@if (Model == DateTime.MinValue)
{
@Html.TextBox("", "", new { @class = "datepicker" })
}
else
{
@Html.TextBox("", String.Format("{0:d/M/yyyy}", Model), new { @class = "datepicker" })
}
所以我为Time.cshtml
创建了一个新的编辑器模板
@model DateTime
@if (Model == DateTime.MinValue)
{
@Html.TextBox("", "", new { @class = "timepicker" })
}
else
{
@Html.TextBox("", String.Format("{0:h\:mm tt}", Model), new { @class = "timepicker" })
}
此时,Html 输入元素会获得不需要的data-val-date
属性,从而导致不显眼的日期验证。 我发现通过添加[DataType(DataType.Time)]
,不再添加data-val-date
。 虽然这是一件好事,但我不是,如果这完全(并且只是(数据类型属性应该做的事情。 所以这是问题的第一部分。
接下来,我为 12 小时时间格式创建了自定义验证规则(服务器和客户端(。
注解:
public class TimeTwelveAttribute : ValidationAttribute, IClientValidatable
{
private const string _ErrorMessage = "Invalid time format. Please use h:mm am/pm (TimeAttribute)";
public TimeTwelveAttribute()
{
this.ErrorMessage = _ErrorMessage;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationRule
{
ErrorMessage = _ErrorMessage,
ValidationType = "timetwelve"
};
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
DateTime time;
if (value == null || !DateTime.TryParse(value.ToString(), out time))
{
return new ValidationResult(_ErrorMessage);
}
return ValidationResult.Success;
}
}
JavaScript
$.validator.unobtrusive.adapters.addSingleVal("timetwelve");
$.validator.addMethod(
"timetwelve",
function (value, element) {
return this.optional(element) || /^((0?[1-9]|1[012])(:[0-5]d){1,2}( ?[AP]M))$/i.test(value);
},
"Invalid time format. Please use h:mm am/pm"
);
但是,将[TimeTwelve]
数据批注添加到 StartTime 不起作用:
- 服务器端验证正在进行,但它正在创建一条我无法覆盖的不同错误消息:"值'asdf'对开始时间无效。
- 如果我在时间字段中键入"asdf",则不会弹出客户端验证消息。
我认为这是由映射到 C# 日期时间的回发值引起的。 无论哪种机制执行,都会具有更高的优先级并覆盖所有其他验证。 作为练习,我将 StartTime 重写为字符串(并更新了模板等(,果然[TimeTwelve]
按预期在客户端和服务器端工作。 我觉得这是堆栈溢出的任何问题都没有涉及的方面。
因此,我问题的第二部分,鉴于我真的更喜欢在我的视图模型中使用 DateTimes,我如何才能让验证工作?
这是我能找到的最接近的问题,但它来自2013年。
首先,若要使客户端验证正常工作,请将脚本更改为
$.validator.unobtrusive.adapters.add('timetwelve', function (options) {
options.rules['timetwelve'] = {};
options.messages['timetwelve'] = options.message;
});
// no need to hard code the message - it will use the one you have defined in the validation atribute
$.validator.addMethod('timetwelve', function (value, element) {
return this.optional(element) || /^((0?[1-9]|1[012])(:[0-5]d){1,2}( ?[AP]M))$/i.test(value);
});
或者您可以使用.addBool()
方法
$.validator.unobtrusive.adapters.addBool("timetwelve");
至于接收值"asdf"对开始时间无效,如果您在文本框中输入"asdf">,这是默认行为。DefaultModelBinder
使用值转换器将Request
中的值分析为属性类型。由于asdf
不能转换为DateTime
,因此添加了ModelState
错误,并且没有进行进一步的验证(因为它已经无效并且没有意义(。当然,一旦客户端验证正常工作,你永远不会达到这一点(除非用户禁用了javascript或它是恶意用户(。
另请注意,您的IsValid()
方法可以简单地
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
return ValidationResult.Success;
}
当您到达此处时,DefaultModelBinder
已经设置了DateTime
值(通过将今天的日期与表单中输入的时间相结合(,因此无需再次尝试解析它。