8

I am new to ASP.Net MVC 3, facing some issues while trying to implementing client side unobtrusive validation for a editor template I have created for showing date in a custom way.

UI
I need to show date in a three texbox UI format as

enter image description here


I have put up a EditorTemplate for displaying the date in three parts as

@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>

Model
I have to bind a Date of Birth field which is a property in a subobject of my model to this property, in this structure

MyModel
    --> MySubModel
          --> DateOfBirth

public class MySubModel
{
    ...

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

    ...
}

Clientside Validation

I have put up a custom validation attribute which implements IClientValidatable as

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;
    }
    ...
}

I am trying to emit the element names of Day, Month & Year textboxes here to client side validation elements, so that I will write a client side jquery validation method and adapter later which would consume those elements and do the validation at the client side.

View
Now, to use this editor template, I put in View the following lines

@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>
...

Added all relevant jquery validation files in the view as references

Questions

  1. This is not emitting out the unobtrusive javascript validation attributes in the html, though I have implemented IClientValidatable. For testing purpose when I put the same attribute (DateTimeClientValidation) on another property in the model which was not using this editor template, then it emitted out those validation attributes, it is not emitting it out only for this editor template. Where could have I gone wrong ?
  2. Regarding Validation Message span for the editor template, is it right that I put it in View only or should I put it directly in the editor template (@Html.ValidationMessageFor(m => m.MySubModel.DateofBirth))
  3. In this example, am I right in the design, I have put in DateTimeClientValidationAttribute, which actually is an attribute I put on model, but this component knows a bit about UI (since it is trying to emit out the Day, Month & Year elements name to the client), this makes Model know a bit about View, am I breaking any design principles here ?
  4. In DateTimeClientValidationAttribute, I am trying to emit out the day, month & year elements names to the client, so that the client script can do validations on it. But since the model property DateofBirth is in a subobject, the actual element name in the script is MySubObject.DateOfBirth, which makes the Day textbox name to be MySubObject.DateofBirth.Day, how can I find that fully qualified model name in the GetClientValidationRules method, so that I can emit out the name to the client ?

Thanks for being patient to read out all this, and for the answers

4

2 回答 2

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>

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

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

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

于 2011-04-06T16:46:15.347 回答
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));
}

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

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;
}, '');
于 2011-04-01T03:03:21.643 回答