如何在部分视图上使用IValidatableObject

时间:2012-09-14 15:06:03

标签: asp.net-mvc-3 validation viewmodel partial-views ivalidatableobject

我似乎无法弄清楚如何验证具有部分ViewModel作为子对象的ViewModel的部分视图片段。这是我的最低级别的部分,它总是被用作其他表单标记内的部分视图:

namespace MVC3App.ViewModels
{
    public class Payment : IValidatableObject
    {
        public decimal Amount { get; set; }
        public int CreditCardNumber { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            if (Amount < 20)
                yield return new ValidationResult("Please pay more than $20", new string[] { "Amount" });
        }
    }
}

这是包含它的'main'ViewModel:

namespace MVC3App.ViewModels
{
    public class NewCustomerWithPayment :IValidatableObject
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public ViewModels.Payment PaymentInfo { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            if (Age < 18)
                yield return new ValidationResult("Too young.", new string[] { "Age" });
        }
    }
}

对于NewCustomerWithPayment的视图,我有这个:

@model MVC3App.ViewModels.NewCustomerWithPayment
@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>NewCustomerWithPayment</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Age)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Age)
            @Html.ValidationMessageFor(model => model.Age)
        </div>
    </fieldset>
    @Html.Partial("Payment")
    <p><input type="submit" value="Create" /></p>
}

部分视图“付款”总是在另一个Html.Beginform标记内呈现,它只是这样:

@model MVC3App.ViewModels.Payment
<h2>Payment</h2>
<fieldset>
    <legend>Payment</legend>

    <div class="editor-label">
        @Html.LabelFor(model => model.Amount)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Amount)
        @Html.ValidationMessageFor(model => model.Amount)
    </div>

    <div class="editor-label">
        @Html.LabelFor(model => model.CreditCardNumber)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.CreditCardNumber)
        @Html.ValidationMessageFor(model => model.CreditCardNumber)
    </div>
</fieldset>

我的问题是我无法在“付款”视图模型上进行验证。任何有经验的人都可以在ViewModel上使用IValidatableObject作为部分视图进行渲染并给我一个有效的验证模式吗?如果必须,我可以在没有JavaScript验证的情况下生活。

2 个答案:

答案 0 :(得分:1)

这些答案都有一些很好的信息,但我的直接问题通过使用这个来解决:

@Html.EditorFor(model => model.PaymentInfo)

而不是:

Html.Partial("Payment", Model.PaymentInfo)

我很惊讶这种方法有效,但确实如此。 EditorFor帮助器像Html.Partial一样呈现部分视图,并自动连接验证。由于某种原因,它确实在子模型上调用了两次验证(在我的示例中为Payment),对于其他人来说似乎是一个报道的问题(http://mvcextensions.codeplex.com/workitem/10),所以我必须在每个模型上包含一个'HasBeenValidated'的布尔值,并在Validate的开头检查它调用

更新:您必须将视图移动到/ Views / Shared /下的EditorTemplates文件夹,以便EditorFor帮助程序使用该视图。否则,EditorFor将为您提供类型的默认编辑字段。

答案 1 :(得分:0)

这是一个复选框的自定义验证器的蹩脚示例:)我会写一个自定义验证器或使用正则表达式。这可能会让您走上正确的道路并且更容易。

 [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class CheckBoxMustBeTrueAttribute : ValidationAttribute, IClientValidatable
{
    #region IClientValidatable Members

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata,
                                                                           ControllerContext context)
    {
        yield return new ModelClientValidationRule
                         {
                             ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
                             ValidationType = "requiredcheckbox"
                         };
    }

    #endregion

    public override bool IsValid(object value)
    {
        if (value is bool)
        {
            return (bool) value;
        }
        return true;
    }
}