ASP.Net MVC 3 - 编辑器模板的客户端不显眼验证

Shr*_*van 7 editortemplates data-annotations unobtrusive-validation asp.net-mvc-3

我是ASP.Net MVC 3的新手,在尝试为我自己创建的用于以自定义方式显示日期的编辑器模板实现客户端不显眼的验证时遇到了一些问题.

UI
我需要以三个texbox UI格式显示日期

在此输入图像描述


我已经建立了一个EditorTemplate,用于显示三个部分的日期

@model DateTime?

<table class="datetime">
<tr>
    <td>@Html.TextBox("Day", (Model.HasValue ? Model.Value.ToString("dd") : string.Empty)) </td>
    <td class="separator">/</td>
    <td>@Html.TextBox("Month", (Model.HasValue ? Model.Value.ToString("MM") : string.Empty))</td>
    <td class="separator">/</td>
    <td>@Html.TextBox("Year", (Model.HasValue ? Model.Value.ToString("yyyy") : string.Empty))</td>
</tr>
<tr>
    <td class="label">dd</td>
    <td/>
    <td class="label">mm</td>
    <td/>
    <td class="label">yyyy</td>
</tr>
</table>
Run Code Online (Sandbox Code Playgroud)

模型
I必须在此结构中将出生日期字段绑定到此属性,该字段是我的模型的子对象中的属性

MyModel
    --> MySubModel
          --> DateOfBirth

public class MySubModel
{
    ...

    [DataType(DataType.Date)]
    [Display(Name = "Date of birth")]
    [DateTimeClientValidation()]
    public DateTime DateofBirth { get; set; }

    ...
}
Run Code Online (Sandbox Code Playgroud)

客户端验证

我已经提出了一个自定义验证属性,它实现了IClientValidatable

public class DateTimeClientValidationAttribute : ValidationAttribute, IClientValidatable
{
    ...

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        List<ModelClientValidationRule> clientRules = new List<ModelClientValidationRule>();

        //Combined date should be valid
        ModelClientValidationRule validDateRule = new ModelClientValidationRule
        {
            ErrorMessage = "Please enter a valid date.",
            ValidationType = "validdate"
        };
        validDateRule.ValidationParameters.Add("dayelement", metadata.PropertyName + ".Day");
        validDateRule.ValidationParameters.Add("monthelement", metadata.PropertyName + ".Month");
        validDateRule.ValidationParameters.Add("yearelement", metadata.PropertyName + ".Year");
        clientRules.Add(validDateRule);

        return clientRules;
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

我试图在这里向客户端验证元素发出日,月和年文本框的元素名称,以便稍后我将编写客户端jquery验证方法和适配器,这将消耗这些元素并在客户端进行验证.


现在查看,要使用此编辑器模板,我将查看以下行

@model MyModel
...
<tr>
    <td class="editor-label">
        @Html.LabelFor(m => m.MySubModel.DateofBirth)
    </td>
    <td class="editor-field">
        @Html.EditorFor(m => m.MySubModel.DateofBirth)
        @Html.ValidationMessageFor(m => m.MySubModel.DateofBirth)
    </td>
</tr>
...
Run Code Online (Sandbox Code Playgroud)

在视图中添加了所有相关的jquery验证文件作为参考

问题

  1. 虽然我已经实现了IClientValidatable,但这并没有在html中发出不显眼的javascript验证属性.出于测试目的,当我将相同的属性(DateTimeClientValidation)放在模型中没有使用此编辑器模板的另一个属性上时,它会发出这些验证属性,它不会仅为此编辑器模板发出它.我哪里可能出错了?
  2. 关于编辑器模板的验证消息范围,我将它放在View中是否正确,或者我应该将其直接放在编辑器模板中(@ Html.ValidationMessageFor(m => m.MySubModel.DateofBirth))
  3. 在这个例子中,我是在设计中,我已经放入了DateTimeClientValidationAttribute,它实际上是我放在模型上的一个属性,但是这个组件对UI有一点了解(因为它试图发出Day,Month和Year元素)客户名称,这使得Model了解了一下View,我在这里打破了任何设计原则吗?
  4. DateTimeClientValidationAttribute中,我试图向客户端发出日,月和年元素名称,以便客户端脚本可以对其进行验证.但由于模型属性DateofBirth位于子对象中,脚本中的实际元素名称是MySubObject.DateOfBirth,这使得Day文本框名称为MySubObject.DateofBirth.Day,如何在GetClientValidationRules方法中找到完全限定的模型名称,以便我可以向客户发出名称?

感谢您耐心地阅读所有这些以及答案

Shr*_*van 10

经过一番努力,我让控制工作,如果对那里的任何人都有帮助,那就投入使用.

第一点是将控件定义为

@model DateTime?

<table class="datetime">
<tr>
    <td>@Html.TextBox("", (Model.HasValue ? Model.Value.ToString("dd") : string.Empty)) </td>
    <td class="separator">/</td>
    <td>@Html.TextBox("", (Model.HasValue ? Model.Value.ToString("MM") : string.Empty))</td>
    <td class="separator">/</td>
    <td>@Html.TextBox("", (Model.HasValue ? Model.Value.ToString("yyyy") : string.Empty))</td>
</tr>
<tr>
    <td class="label">dd</td>
    <td/>
    <td class="label">mm</td>
    <td/>
    <td class="label">yyyy</td>
</tr>
</table>
Run Code Online (Sandbox Code Playgroud)

确保您将空字符串作为名称提供给所有三个文本框,这会强制MVC框架为客户端中的所有三个文本框生成与Model DateofBirth对应的相同名称.此外,将为列表中的第一个文本框生成不显眼的javascript验证参数,因为它是具有模型名称的编辑控件的第一个出现.

在客户端,任何这些文本框上的任何事件都会触发所有相关的验证事件,因为所有这3个文本框都具有相同的名称.

除了必需的常规验证之外,....我们必须编写一个自定义客户端验证例程,它将组合所有这三个值并检查组合值是否构成有效日期.这可以通过Jeff给出的答案来实现.


Jef*_*ley 5

好吧,好几件事.首先 - 如果可能的话,不要将日期分成三个字段.只需将其设置为具有一些验证的单个字段并且可能是jQueryUI的DatePicker,您将为自己省去一个令人头痛的世界.

接下来,在DateTimeClientValidationAttribute内的自定义属性代码中,您实际上从未通过实现IsValid来检查日期是否有效:

protected override ValidationResult IsValid(object value, 
    ValidationContext validationContext) 
{
   ...
   // Put date parts together and check is valid...
   if (DateTime.TryParse(year+"/"+month+"/"+day, out dateResult))
      return ValidationResult.Success;

   // Not valid
   return new ValidationResult(String.Format(ErrorMessageString, 
      validationContext.DisplayName));
}
Run Code Online (Sandbox Code Playgroud)

接下来,您将不得不创建一个不显眼的验证适配器和一个jQuery验证器方法,它将params放在一起并尝试解析日期:

jQuery.validator.unobtrusive.adapters.add(
    'validdate', // notice this is coming from how you named your validation rule
    ['dayelement'],
    ['monthelement'],
    ['yearelement']
    function (options) {
        options.rules['datepartcheck'] = options.params;
        options.messages['datepartcheck'] = options.message;
    }
);
jQuery.validator.addMethod('datepartcheck', function (value, element, params) {
    var year = params[2];
    var month = params[1];
    var day = params[0];

    var birthDate = year + '/' + month-1 + '/' + day;
    var isValid = true;

    try {
        // datepicker is a part of jqueryUI.
        // include it and you can take advantage of .parseDate:
        $.datepicker.parseDate('yy/mm/dd', birthDate);
    }
    catch (error) {
        isValid = false;
    }
    return isValid;
}, '');
Run Code Online (Sandbox Code Playgroud)