Shawn
Shawn

Reputation: 1891

MVC3 custom unobtrusive validation - force validation from checkbox

I have the following class used for custom validation:

[AttributeUsage(AttributeTargets.Property, AllowMultiple=false, Inherited=true)]
public sealed class RequiredIfAnyTrueAttribute : ValidationAttribute, IClientValidatable
{
    private const string DefaultErrorMessage = "{0} is required";

    public List<string> OtherProperties { get; private set; }

    public RequiredIfAnyTrueAttribute(string otherProperties)
        : base(DefaultErrorMessage)
    {
        if (string.IsNullOrEmpty(otherProperties))
            throw new ArgumentNullException("otherProperty");

        OtherProperties = new List<string>(otherProperties.Split(new char[] { '|', ',' }));
    }

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

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value == null)
        {
            foreach (string s in OtherProperties)
            {
                var otherProperty = validationContext.ObjectType.GetProperty(s);
                var otherPropertyValue = otherProperty.GetValue(validationContext.ObjectInstance, null);

                if (otherPropertyValue.Equals(true))
                    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 = "requiredifanytrue"
        };

        clientValidationRule.ValidationParameters.Add("otherproperties", string.Join("|",OtherProperties));

        return new[] { clientValidationRule };
    }
}

My ViewModel is:

public class SampleViewModel
{
    public bool PropABC { get; set; }
    public bool PropXYZ { get; set; }

    [RequiredIfAnyTrue("PropABC|PropXYZ")]
    public int? TestField { get; set; }
}

When my strongly typed view renders, everything sees to work fine. If PropABC or PropXYZ is selected then I am required to enter a value for TestField. Both client and server-side validation is functional.

However, given the following sequence of events:

  1. check PropABC
  2. submit form
  3. client-side validation fires for TestField required
  4. uncheck PropABC
  5. client validation does not re-fire and validation message remains until form submit

In order to resolve #5 I would typically attach click events to the checkboxes via jquery onready to refire the validation.

Is there a preferred/recommended way to manually force client-side validation given MVC3 + unobstrusive + jquery?

Upvotes: 0

Views: 1894

Answers (3)

Eric Gontier
Eric Gontier

Reputation: 131

FoolProof is still in beta and does not work with nested viewmodel, also with arrays

Upvotes: 0

counsellorben
counsellorben

Reputation: 10924

Shawn, attaching to events is the best approach to get validation to refire.

I would suggest creating a class called "validate" (or something along those lines), adding it to each element to be validated, and then use jQuery to attach to the click and blur events (and possibly the change event) of each element with that class, and validate the element like so:

$("form").validate().element(this);

Upvotes: 1

Doug Chamberlain
Doug Chamberlain

Reputation: 11341

Do you need to write your own attributes? If not I think you may be able to avoid "reinventing the wheel"

FoolProof works great. you get get it as a NuGet package.

NuGet: install-package foolproof

It includes a lot of greate attributes for various combinations of on-the-fly required fields and such.

Upvotes: 1

Related Questions