仅限时间的编辑器模板和验证

时间:2017-08-24 05:20:33

标签: c# asp.net-mvc datetime asp.net-mvc-5

在使用不显眼的验证创建仅限时间的输入字段时遇到问题,映射到不可为空的DateTime字段。

我正在使用数据库优先方法。数据库具有用于存储日期和时间的日期时间字段。我的意思是一些日期时间有实际日期数据,00:00:00时间,而其他日期数据有实时无意义日期数据。我理解这是由于EF5和C#数据类型的限制。现在使用EF6,但并不是特意打算更改数据库。

这是视图模型

[UIHint("Date")]
public DateTime MatchDate { get; set; }

[UIHint("Time")]
public DateTime StartTime { get; set; }

仅限日期字段MatchDate正在运行。它使用EditorFor模板Date.cshtml(下面)。它允许文本输入,正确附加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

创建了一个新的EditorFor模板
@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#DateTimes引起的。无论什么机制都需要更高的优先级并覆盖所有其他验证。作为练习,我将StartTime重新编写为字符串(并更新了模板等),确保[TimeTwelve]正常工作,客户端和服务器端按预期工作。我觉得这个方面在stackoverflow上的任何相关问题都没有涉及。

所以我的问题的第二部分,鉴于我真的更喜欢在我的视图模型中使用DateTimes,我如何才能使验证工作?

This是我能找到的最接近的问题,但是从2013年开始。

1 个答案:

答案 0 :(得分:2)

首先,要使客户端验证工作,请将脚本更改为

$.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");

至于接收值&#39; asdf&#39;如果您输入&#34; asdf&#34;对于开始时间无效在文本框中,这是默认行为。 DefaultModelBinder使用ValueConverters将Request中的值解析为属性类型。由于asdf无法转换为DateTime,因此会添加ModelState错误,并且不会进行进一步的验证(因为它已经无效并且没有任何意义)。当然,一旦客户端验证工作正常,您无论如何都不会达到这一点(除非用户已禁用javascript或其恶意用户)。

另请注意,您的IsValid()方法可以只是

protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
    return ValidationResult.Success;
}

当您到达此处时,DefaultModelBinder已设置DateTime值(通过将今天的日期与表单中输入的时间相结合),因此无需尝试再次解析它。