MVC3不显眼的验证组输入

时间:2011-04-28 09:15:08

标签: asp.net-mvc asp.net-mvc-3

我需要验证3个或更多输入字段(至少需要一个)。例如,我有电子邮件,传真,电话。

我要求至少填写一个。我需要服务器和客户端'不显眼的验证'。请帮忙。我查看了“比较”方法并尝试修改它但没有运气。请帮忙。 感谢

4 个答案:

答案 0 :(得分:84)

您可以编写自定义属性:

public class AtLeastOneRequiredAttribute : ValidationAttribute, IClientValidatable
{
    private readonly string[] _properties;
    public AtLeastOneRequiredAttribute(params string[] properties)
    {
        _properties = properties;
    }

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

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

            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 = "atleastonerequired"
        };
        rule.ValidationParameters["properties"] = string.Join(",", _properties);

        yield return rule;
    }
}

可用于装饰您的一个视图模型属性(如果验证失败,您希望突出显示该属性):

public class MyViewModel
{
    [AtLeastOneRequired("Email", "Fax", "Phone", ErrorMessage = "At least Email, Fax or Phone is required")]
    public string Email { get; set; }
    public string Fax { get; set; }
    public string Phone { get; set; }
}

然后是一个简单的控制器:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new MyViewModel();
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(MyViewModel model)
    {
        return View(model);
    }
}

呈现以下视图,该视图将负责定义自定义客户端验证器适配器:

@model MyViewModel

<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script type="text/javascript">
    jQuery.validator.unobtrusive.adapters.add(
        'atleastonerequired', ['properties'], function (options) {
            options.rules['atleastonerequired'] = options.params;
            options.messages['atleastonerequired'] = options.message;
        }
    );

    jQuery.validator.addMethod('atleastonerequired', function (value, element, params) {
        var properties = params.properties.split(',');
        var values = $.map(properties, function (property, index) {
            var val = $('#' + property).val();
            return val != '' ? val : null;
        });
        return values.length > 0;
    }, '');
</script>

@using (Html.BeginForm())
{
    @Html.ValidationSummary(false)

    <div>
        @Html.LabelFor(x => x.Email)
        @Html.EditorFor(x => x.Email)
    </div>

    <div>
        @Html.LabelFor(x => x.Fax)
        @Html.EditorFor(x => x.Fax)
    </div>

    <div>
        @Html.LabelFor(x => x.Phone)
        @Html.EditorFor(x => x.Phone)
    </div>

    <input type="submit" value="OK" />
}

当然,自定义适配器和验证器规则应该外部化为单独的javascript文件,以避免脚本与标记混合。

答案 1 :(得分:5)

我花了36个多小时为什么代码对我不起作用..最后,我发现在我的情况下,我不应该在这行代码中使用属性名称

[AtLeastOneRequired("Email", "Fax", "Phone", ErrorMessage = "At least Email, Fax or Phone is required")]

但我必须使用HTMl元素ID来代替属性名称,它就像魔法一样。

如果它可能对某人有帮助,请在此处发布。

答案 2 :(得分:2)

由于您使用的是MVC 3,请查看great video Brad Wilson对mvcConf的看法。创建客户端+服务器Unobtrusive Validation所需的一切

答案 3 :(得分:1)

@Darin Kimitrov的解决方案可能是创建自定义验证属性的标准,该属性适用于不显眼的验证。但是,使用自定义验证属性进行不显眼的验证有一些缺点,例如:

  • 自定义验证属性仅附加到一个属性,因此如果其他两个输入上有更改事件,则客户端验证将不起作用。
  • 错误消息适用于ValidationSummary,但如果要为整个组显示1条错误消息(我认为这是常态),那几乎是不可能的。
  • 为了避免第一个问题,我们可以为组中的每个元素添加自定义验证属性,这将导致另一个问题:我们必须验证组的所有元素,而不是停留在第一个无效组元素。当然,第二个问题 - 每个元素的单独错误消息 - 仍然存在。

还有另一种方法可以使用jquery验证器(https://jqueryvalidation.org/validate/#groups)中的组设置来处理输入组的客户端验证。唯一的问题(也是一个大问题)是默认情况下不显眼的验证不支持jquery验证的组,所以我们必须自定义一点。

虽然这个答案几乎不“不引人注目”,但在我看来,如果你的最终目标是在使用微软不显眼的验证器库的同时验证一组输入,那么值得尝试摆脱不必要的代码复杂化。

首先,因为jquery unobtrusive验证器中没有默认jquery验证器的组设置,我们必须覆盖不显眼的设置(参考How can I customize the unobtrusive validation in ASP.NET MVC 3 to match my style?

$("form").on('submit', function () {
    var form = this;
    var validator = $(this).data("validator");

    if (validator.settings && !validator.settings.submitHandler) {
        $.extend(true, validator.settings.rules, validationSettings.rules);
        $.extend(true, validator.settings.groups, validationSettings.groups);
        initGroups(validator);

        var fnErrorReplacement = validator.settings.errorPlacement;
        validator.settings.errorPlacement = function (error, element) {
            validationSettings.errorPlacement(error, element, fnErrorReplacement, form);
        }
        validator.settings.submitHandler = formSubmitHandler;
    }
});

function formSubmitHandler(form) {
    form.submit();
}

之后,覆盖不显眼的验证者的组,规则和errorPlacement设置。

var validationSettings = {
groups: {
    checkboxgroup: "Email Fax Phone"
},
rules: {
    Email: {
        required: function () {
            return validateCheckboxGroup(["#Email", "#Fax", "#Phone"]);
        }
    },
    Fax: {
        required: function () {
            return validateCheckboxGroup(["#Email", "#Fax", "#Phone"]);
        }
    },
    Phone: {
        required: function () {
            return validateCheckboxGroup(["#Email", "#Fax", "#Phone"]);
        }
    }
}
,
errorPlacement: function (error, element, fnUnobtrusive, form) {
    switch (element.attr("name")) {
        case "Email":
        case "Fax":
        case "Phone":
            onGroupError(error, "CheckBoxGroup", form);
            break;
        default:
            fnUnobtrusive(error, element);
            break;
    }
}
}

function validateCheckboxGroup(names) {
var result = true;
$.each(names, function (index, value) {
    if ($(value).is(":checked")) {
        result = false;
    }
});
return result;
}

因为不显眼的验证器没有实现jquery验证器的组设置,我们需要重用两个库中的两个函数:(1).split组名(重用jquery验证器中的代码)和(2)追加错误元素而不删除'input-validation-error'类(从不显眼的库中重用函数onError)。

function initGroups(validators) {
validators.groups = {};
$.each(validators.settings.groups,
    function (key, value) {
        if (typeof value === "string") {
            value = value.split(/\s/);
        }
        $.each(value,
            function (index, name) {
                validators.groups[name] = key;
            });
    });
}

function onGroupError(error, inputElementName, form) {
var container = $(form).find("[data-valmsg-for='" + inputElementName + "']"),
replaceAttrValue = container.attr("data-valmsg-replace"),
replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null;

container.removeClass("field-validation-valid").addClass("field-validation-error");
error.data("unobtrusiveContainer", container);

if (replace) {
    container.empty();
    error.appendTo(container);
}
else {
    error.hide();
}
}

最后,使用HtmlExtensions.ValidationMessage创建复选框组的错误范围。

@Html.ValidationMessage("CheckBoxGroup", new { @class = "text-danger" }) 

保持“input-validation-error”类是必要的,这样jquery验证器将验证复选框组的所有3个元素(电子邮件,电话,传真)作为一个整体,而不是逐个验证。不显眼的验证库默认在函数onError上删除此类,因此我们必须在上面的函数onGroupError中将其自定义为showne。