发送IEnumerable <t> </t>时,对EditorTemplate进行客户端不显眼的验证

时间:2015-02-27 01:52:39

标签: asp.net asp.net-mvc unobtrusive-validation fluentvalidation mvc-editor-templates

我可能无法通过搜索找到解决方案或缺乏解决方案。也许我没有正确地说出来,但是当我将EditorTemplate传递给IEnumerable<T>时,我的问题是尝试在[Validator(typeof(ParentModelValidator))] public class ParentModel { ... public IEnumerable<ChildModel> ChildModels { get; set; } } public class ParentModelValidator : AbstractValidator<ParentModel> { public ParentModelValidator() { RuleFor(x => x.ChildModels).SetCollectionValidator(new ChildModelValidator()); } } 上触发客户端不显眼的验证。我的设置:

ParentModel.cs

[Validator(typeof(ChildModelValidator))]
public class ChildModel
{
     public bool IsRequired { get; set; }
     public string foo { get; set; }
}

public class ChildModelValidator : AbstractValidator<ChildModel>
{
    public ChildModelValidator ()
    {
        RuleFor(x => x.foo)
            .NotEmpty().When(x => x.IsRequired);
    }
}

ChildModel.cs

@model ParentModel

@using (Html.BeginForm("Index", "Application", FormMethod.Post))
{
    @Html.AntiForgeryToken()
    @Html.Partial("_Parent", Model)
    @Html.EditorFor(m => m.ChildModels)
    <input type="submit" value="submit" />
}

ParentShell.cshtml

_Parent

@Html.TextBoxFor(m => m.bar)部分只包含少数常见的,可重复使用的@Html.ValidationMessageFor(m => m.bar)EditorTemplate字段。

ChildModel.cshtml @model ChildModel @Html.TextBoxFor(m => m.foo) @if (Model.IsRequired) { @Html.ValidationMessageFor(m => m.foo) }

_Parent

客户端验证会触发IsRequired部分中的所有字段,但ValidationMessageFor为真且我应该有EditorTemplate时,我什么也得不到。这是IEnumerable<T>收到ChildModels[0].foo的客户端不显眼验证的已知约束吗?这是因为在渲染过程中插入了索引器(ChildModels_0__.foo和{{1}})吗?

1 个答案:

答案 0 :(得分:3)

来自FluentValidation

的文档
  

请注意,FluentValidation也适用于ASP.NET MVC的客户端验证,但并非所有规则都受支持。例如,使用条件(使用When / Until),自定义验证器或对Must的调用定义的任何规则都不会在客户端运行

由于您使用了.When条件,因此无法获得客户端验证。

使用foolproof [RequiredIfTrue]属性等替代方法适用于简单属性,但不适用于复杂对象或集合。

您可以通过创建实现ValidationAttribute

的自定义IClientValidatable来解决此问题
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class ComplexRequiredIfTrue : ValidationAttribute, IClientValidatable
{
    private const string _DefaultErrorMessage = "The {0} field is required.";
    public string OtherProperty { get; private set; }
    public ComplexRequiredIfTrue(string otherProperty) : base(_DefaultErrorMessage)
    {
        if (string.IsNullOrEmpty(otherProperty))
        {
            throw new ArgumentNullException("otherProperty");
        }
        OtherProperty = otherProperty;
    }

    public override string FormatErrorMessage(string name)
    {
        return string.Format(ErrorMessageString, name, OtherProperty);
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value == null)
        {
            PropertyInfo otherProperty = validationContext.ObjectInstance.GetType().GetProperty(OtherProperty);
            bool isRequired = (bool)otherProperty.GetValue(validationContext.ObjectInstance, null);
            if (isRequired)
            {
                return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
            }
        }
        return ValidationResult.Success;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var clientValidationRule = new ModelClientValidationRule()
        {
            ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
            ValidationType = "complexrequirediftrue"
        };
        clientValidationRule.ValidationParameters.Add("otherproperty", OtherProperty);
        return new[] { clientValidationRule };
    }
}

以及相关的脚本

function nameToIndex (value) {
  return value.replace(/[\[\].]/g, '_');
}

(function ($) {
  $.validator.addMethod("complexrequirediftrue", function (value, element, params) {
    // We need to get the prefix of the control we are validating
    // so we can get the corresponding 'other property'
    var name = $(element).attr('name');
    var index = name.lastIndexOf('.');
    var prefix = nameToIndex(name.substr(0, index + 1));
    var otherProp = $('#' + prefix + params);
    if (otherProp.val() == "True" && !value) {
      return false;
    }
    return true;
  });
  $.validator.unobtrusive.adapters.addSingleVal("complexrequirediftrue", "otherproperty");
}(jQuery));

然后将其应用于您的财产

public class ChildModel
{
  public bool IsRequired { get; set; }
  [ComplexRequiredIfTrue("IsRequired")]
  public string foo { get; set; }
}

并在EditorTemplate中添加@Html.HiddenFor(m => m.IsRequired)

@model ChildModel
@Html.HiddenFor(m => m.IsRequired)
@Html.TextBoxFor(m => m.foo)
@Html.ValidationMessageFor(m => m.foo)

编辑:继续评论,如果控制器是

model.ChildModels = new List<ChildModel>() { new ChildModel() { IsRequired = true }, new ChildModel() };
return View(model);

然后单击提交按钮时生成的html为:

<input data-val="true" data-val-required="The IsRequired field is required." id="ChildModels_0__IsRequired" name="ChildModels[0].IsRequired" type="hidden" value="True">
<input class="input-validation-error" data-val="true" data-val-complexrequirediftrue="The foo field is required." data-val-complexrequirediftrue-otherproperty="IsRequired" id="ChildModels_0__foo" name="ChildModels[0].foo" type="text" value="">
<span class="field-validation-error" data-valmsg-for="ChildModels[0].foo" data-valmsg-replace="true">The foo field is required.</span>
<input data-val="true" data-val-required="The IsRequired field is required." id="ChildModels_1__IsRequired" name="ChildModels[1].IsRequired" type="hidden" value="False">
<input data-val="true" data-val-complexrequirediftrue="The foo field is required." data-val-complexrequirediftrue-otherproperty="IsRequired" id="ChildModels_1__foo" name="ChildModels[1].foo" type="text" value="">
<span class="field-validation-valid" data-valmsg-for="ChildModels[1].foo" data-valmsg-replace="true"></span>

请注意,表单未提交,并且第一个文本框显示错误消息