14

我有一个视图模型,其中包含某人出生日期的年/月/日属性。所有这些字段都是必需的。现在,如果有人没有输入出生日期的任何内容,他们会收到 3 条单独的错误消息。

出生日期字段

我想要做的是以某种方式将这些错误消息组合成一条消息,上面写着“需要出生日期”。因此,如果这些字段中有 1 个或多个为空白,它们将始终只收到 1 条验证消息。

我需要通过 jquery validate 和 unobtrusive validate 进行客户端验证。通过查看这个问题,我知道使用 jquery validate 插件可以做到这一点。但是我不知道如何使用我的模型上的验证属性和不显眼的验证来使用 asp.net mvc 来实现这一点。希望有一些内置的方法可以对属性进行分组以进行验证,但是如果没有,可以使用自定义验证属性来完成吗?

这是我现有的模型和视图的样子:

该模型:

public class MyModel {
    [Required(ErrorMessage = "Year is required")]
    public int Year { get; set; }
    [Required(ErrorMessage = "Month is required")]
    public int Month { get; set; }
    [Required(ErrorMessage = "Day is required")]
    public int Day { get; set; }
}

风景:

<div>
    <label>Date of birth: <span style="color:red;">*</span></label>
    <div>@Html.DropDownListFor(m => m.Year, ApplicationModel.GetSelectListForDateRange(DateTime.Today.Year - 16, DateTime.Today.Year - 10), "", new{data_description="birthDate"})@Html.LabelFor(m => m.StudentBirthYear)</div>
    <div>@Html.DropDownListFor(m => m.Month, ApplicationModel.GetSelectListForDateRange(1, 12, true), "", new{data_description="birthDate"})@Html.LabelFor(m => m.StudentBirthMonth)</div>
    <div>@Html.DropDownListFor(m => m.Day, ApplicationModel.GetSelectListForDateRange(1, 31), "", new{data_description="birthDate"})@Html.LabelFor(m => m.StudentBirthDay)</div>
</div>
<div class="error-container">@Html.ValidationMessageFor(m => m.Year)</div>
<div class="error-container">@Html.ValidationMessageFor(m => m.Month)</div>
<div class="error-container">@Html.ValidationMessageFor(m => m.Day)</div>
4

4 回答 4

9

我参加聚会有点晚了(只有几年)仍然......

最合适的解决方案确实是创建一个CustomAttribute,但不是给你很好的建议,我会告诉你如何去做。

自定义属性:

public class GroupRequiredAttribute : ValidationAttribute, IClientValidatable
{
    private readonly string[] _serverSideProperties;

    public GroupRequiredAttribute(params string[] serverSideProperties)
    {
        _serverSideProperties = serverSideProperties;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (_serverSideProperties == null || _serverSideProperties.Length < 1)
        {
            return null;
        }

        foreach (var input in _serverSideProperties)
        {
            var propertyInfo = validationContext.ObjectType.GetProperty(input);
            if (propertyInfo == null)
            {
                return new ValidationResult(string.Format("unknown property {0}", input));
            }

            var propertyValue = propertyInfo.GetValue(validationContext.ObjectInstance, null);
            if (propertyValue is string && !string.IsNullOrEmpty(propertyValue as string))
            {
                return null;
            }

            if (propertyValue != null)
            {
                return null;
            }
        }

        return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = ErrorMessage,
            ValidationType = "grouprequired"
        };

        rule.ValidationParameters["grouprequiredinputs"] = string.Join(",", this._serverSideProperties);

        yield return rule;
    }
}

ViewModel:仅在 viewModel 上装饰一个字段,如下所示:

    [GroupRequired("Year", "Month", "Day", ErrorMessage = "Please enter your date of birth")]
    public int? Year { get; set; }

    public int? Month { get; set; }

    public int? Day { get; set; }

Jquery:在我的情况下,您需要添加适配器,jquery.validate.unobtrusive.customadapters.js或者在您注册适配器的任何地方添加适配器(您可以将其放在页面上,只需在不显眼的验证运行后执行)。

(function ($) {
    jQuery.validator.unobtrusive.adapters.add('grouprequired', ['grouprequiredinputs'], function (options) {
        options.rules['grouprequired'] = options.params;
        options.messages['grouprequired'] = options.message;
    });
}(jQuery));

jQuery.validator.addMethod('grouprequired', function (value, element, params) {
    var inputs = params.grouprequiredinputs.split(',');
    var values = $.map(inputs, function (input, index) {
        var val = $('#' + input).val();
        return val != '' ? input : null;
    });
    return values.length == inputs.length;
});

那应该这样做。

对于那些对它的作用感兴趣的人:在 C# 领域中,它会抓取字段的 id 并将它们粘合起来,,并将其放入 Year 字段的自定义属性中。

HTML应该看起来像这样(如果它不调试 C# 属性):

<input class="tooltip form-control input dob--input-long" data-val="true" data-val-grouprequired="Please enter your date of birth" data-val-grouprequired-grouprequiredinputs="Year,Month,Day" name="Year" placeholder="YYYY" tabindex="" type="text" value="">

然后 Jquery 验证将它们拆分回 id 并检查它们是否都不为空,仅此而已。

您将希望以某种方式将字段标记为无效(现在它只会标记字段属性位于)最合适的解决方案恕我直言,将所有字段包装在带有类的容器中field-error-wrapper,然后在加载 Jquery 验证后将以下内容添加到您的页面:

$.validator.setDefaults({
    highlight: function (element) {
        $(element).closest(".field-error-wrapper").addClass("input-validation-error");
    },
    unhighlight: function (element) {
        $(element).closest(".field-error-wrapper").removeClass("input-validation-error");
    }
});

它不会标记字段,而是标记容器,然后你可以编写你的 CSS,如果容器被标记,.input-validation-error那么里面的所有字段都会变成红色。我想我在这里的工作已经完成了。

编辑:好的,所以似乎还有一个问题是字段未被标记,因为验证器认为日期和月份是有效的,它需要从父级中删除无效类,验证器首先标记无效字段然后取消标记有效,这导致验证不被突出显示,所以我改变了验证发生的顺序,我不建议全局覆盖它(因为我不确定它可能会产生什么灾难性的影响),只需将它粘贴到你有生日字段的页面上。

$(function () {
    $.data($('form')[0], 'validator').settings.showErrors = function () {
        if (this.settings.unhighlight) {
            for (var i = 0, elements = this.validElements() ; elements[i]; i++) {
                this.settings.unhighlight.call(this, elements[i], this.settings.errorClass, this.settings.validClass);
            }
        }
        this.hideErrors();
        for (var i = 0; this.errorList[i]; i++) {
            var error = this.errorList[i];
            this.settings.highlight && this.settings.highlight.call(this, error.element, this.settings.errorClass, this.settings.validClass);
            this.showLabel(error.element, error.message);
        }
        if (this.errorList.length) {
            this.toShow = this.toShow.add(this.containers);
        }
        if (this.settings.success) {
            for (var i = 0; this.successList[i]; i++) {
                this.showLabel(this.successList[i]);
            }
        }
        this.toHide = this.toHide.not(this.toShow);

        this.addWrapper(this.toShow).show();
    };
});

希望这可以节省您一些时间。

于 2015-12-02T10:52:29.787 回答
0

您应该实施IValidatableObject并采用Require. 然后服务器端的验证将完成这项工作,例如:

public class MyModel : IValidatableObject
{
  public int Year { get; set; }
  public int Month { get; set; }
  public int Day { get; set; }

  public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
  {
      if (/*Validate properties here*/) yield return new ValidationResult("Invalid Date!", new[] { "valideDate" });
  }
}

对于客户端验证,您需要实现自己的功能,并以某种方式向用户提示错误。

编辑:鉴于您仍然需要客户端验证,您应该执行以下操作:

$("form").validate({
    rules: {
        Day: { required: true },
        Month : { required: true },
        Year : { required: true }
    },
    groups: {
        Date: "Day Month Year"
    },
   errorPlacement: function(error, element) {
       if (element.attr("id") == "Day" || element.attr("id") == "Month" || element.attr("id") == "Year") 
        error.insertAfter("#Day");
       else 
        error.insertAfter(element);
   }
});
于 2013-09-10T19:33:04.610 回答
0

您可以简单地使用CustomAttribute来做到这一点。

只需将此属性放在您的模型上

[CustomValidation(typeof(MyModel), "ValidateRelatedObject")]

然后只需定义规则以验证以下方法中的值:

public static ValidationResult ValidateRelatedObject(object value, ValidationContext context)
{
    var context = new ValidationContext(value, validationContext.ServiceContainer, validationContext.Items);
    var results = new List<ValidationResult>();
    Validator.TryValidateObject(value, context, results);

    // TODO: Wrap or parse multiple ValidationResult's into one ValidationResult

    return result; 
}

欲了解更多信息,您可以访问此链接

于 2013-09-10T19:47:48.730 回答
0

嗨,我希望这能满足您的要求

//--------------------------HTML Code-----------------------------
    <form id="myform">  
        <select name="day">
            <option value="">select</option>
            <option value="1">1</option>
        <select>
             <select name="mnth">
            <option value="">select</option>
            <option value="Jan">Jan</option>
        <select>
             <select name="yr">
            <option value="">select</option>
            <option value="2015">2015</option>
        <select>
            <br/>
        <input  type="submit"  />
                    <br/>
            <div id="msg"></div>
    </form>



    //-------------------------------JS Code to validate-------------



      $(document).ready(function() {


    $('#myform').validate({
        rules: {
            day: {
                required: true
            },
            mnth: {
                required: true
            },
             yr: {
                required: true
            }
        },
          errorPlacement: function (error, element) {
            var name = $(element).attr("name");
            error.appendTo($("#msg"));
        },
        messages: {
            day: {
                required: "Date of birth is required"
            },
            mnth: {
                required: "Date of birth is required"
            },
             yr: {
                required: "Date of birth is required"
            }
        },
        groups: {
            p: "day mnth yr"
        },
        submitHandler: function(form) { // for demo
            alert('valid form');
            return false;
        }
    });

});

这是运行示例

于 2015-01-30T20:24:26.007 回答