MVC3 Validation - Require One From Group

后端 未结 4 2204
梦毁少年i
梦毁少年i 2020-11-29 19:28

Given the following viewmodel:

public class SomeViewModel
{
  public bool IsA { get; set; }
  public bool IsB { get; set; }
  public bool IsC { get; set; } 
         


        
4条回答
  •  既然无缘
    2020-11-29 19:49

    Here's one way to proceed (there are other ways, I am just illustrating one that would match your view model as is):

    [AttributeUsage(AttributeTargets.Property)]
    public class RequireAtLeastOneOfGroupAttribute: ValidationAttribute, IClientValidatable
    {
        public RequireAtLeastOneOfGroupAttribute(string groupName)
        {
            ErrorMessage = string.Format("You must select at least one value from group \"{0}\"", groupName);
            GroupName = groupName;
        }
    
        public string GroupName { get; private set; }
    
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            foreach (var property in GetGroupProperties(validationContext.ObjectType))
            {
                var propertyValue = (bool)property.GetValue(validationContext.ObjectInstance, null);
                if (propertyValue)
                {
                    // at least one property is true in this group => the model is valid
                    return null;
                }
            }
            return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
        }
    
        private IEnumerable GetGroupProperties(Type type)
        {
            return
                from property in type.GetProperties()
                where property.PropertyType == typeof(bool)
                let attributes = property.GetCustomAttributes(typeof(RequireAtLeastOneOfGroupAttribute), false).OfType()
                where attributes.Count() > 0
                from attribute in attributes
                where attribute.GroupName == GroupName
                select property;
        }
    
        public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var groupProperties = GetGroupProperties(metadata.ContainerType).Select(p => p.Name);
            var rule = new ModelClientValidationRule
            {
                ErrorMessage = this.ErrorMessage
            };
            rule.ValidationType = string.Format("group", GroupName.ToLower());
            rule.ValidationParameters["propertynames"] = string.Join(",", groupProperties);
            yield return rule;
        }
    }
    

    Now, let's define a controller:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            var model = new SomeViewModel();
            return View(model);        
        }
    
        [HttpPost]
        public ActionResult Index(SomeViewModel model)
        {
            return View(model);
        }
    }
    

    and a view:

    @model SomeViewModel
    
    
    
    
    @using (Html.BeginForm())
    {
        @Html.EditorFor(x => x.IsA)
        @Html.ValidationMessageFor(x => x.IsA)
        
    @Html.EditorFor(x => x.IsB)
    @Html.EditorFor(x => x.IsC)
    @Html.EditorFor(x => x.IsY) @Html.ValidationMessageFor(x => x.IsY)
    @Html.EditorFor(x => x.IsZ)
    }

    The last part that's left would be to register adapters for the client side validation:

    jQuery.validator.unobtrusive.adapters.add(
        'group', 
        [ 'propertynames' ],
        function (options) {
            options.rules['group'] = options.params;
            options.messages['group'] = options.message;
        }
    );
    
    jQuery.validator.addMethod('group', function (value, element, params) {
        var properties = params.propertynames.split(',');
        var isValid = false;
        for (var i = 0; i < properties.length; i++) {
            var property = properties[i];
            if ($('#' + property).is(':checked')) {
                isValid = true;
                break;
            }
        }
        return isValid;
    }, '');
    

    Based on your specific requirements the code might be adapted.

提交回复
热议问题