Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC 4 Custom data annotation manual jquery validation call

We are trying to create some custom validation with MVC 4 data annotations, the validation we are creating is kind of message prompts more than restrictive validation. First of all we have created some custom validation classes inheriting from the ValidationAttribute Class and overriding the IsValid() method to test the data and return an ValidationResult if not valid. The view that displays this data has Partial views that use EditorTemplates to display razor generated data using our custom data annotations and many built in validation, all this is wrapped in a form like this

@using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl }))

Our requirements are to let the user post back the data and partially save the forms but prompt them on any invalid fields so are using the CSS class on the form submit to allow postback like so

<input type="submit" value="Save" class="cancel"/>

All this is working fine but we have now requirements to display all the error messages on page load which I did not see to be a problem until I tried it…

I found a few examples that used jquery in the $(document).ready event that called the form valid methods as seen here

Manual form validation in MVC 3 and JQuery

but this did not seem to work for us the $(‘form’).Validate() does not seem to do anything the only call that seems to fire the forms validation is $(‘form’).valid() But this only seems to show the built in validation such as the [Required] attributes and the only way to get the custom validation messages to show is to use the submit button to post back the form.

There must be a way to get my custom data annotations to display the messages without posting back the page the first time right? Any help would be greatly appreciated.

like image 943
Troublesum Avatar asked Aug 13 '12 09:08

Troublesum


1 Answers

Ok so i have found a way to get the result i wanted although it was a little more work than I wanted/ imagined it would be. The stuff I was missing was I didn’t implament IClientValidatable in my custom validation class and had to add my custom validation to the jQuery Validator addmethod which I had tried but not with the IClientValidatable implamted in the custom validation class, I will quickly run through how to get this working im assuming you have all the jQuery stuff set up / included

First create simple model that uses a custom validation attribute

public class Person
{
    [Required]
    [Display( Name="Name")]
    public string Name { get; set; }
    public int Age { get; set; }

    //Uses a custom data annotation that requires that at lease it self or the property name passed in the constructor are not empty
    [OneOfTwoRequired("Mobile")]
    public string Phone { get; set; }
    [OneOfTwoRequired("Phone")]
    public string Mobile { get; set; }
}

The custom validation class that uses reflection to get the property of the string name passed in to test with

Note as of 15/08/2012 : if you are using MVC 4 you will need to of referenced System.web.mvc 3.0 for the use of IClientValidatable as ModelClientValidationRule does no seem to exist in MVC 4

public class OneOfTwoRequired : ValidationAttribute, IClientValidatable
    {
        private const string defaultErrorMessage = "{0} or {1} is required.";

        private string otherProperty;

        public OneOfTwoRequired(string otherProperty)
            : base(defaultErrorMessage)
        {
            if (string.IsNullOrEmpty(otherProperty))
            {
                throw new ArgumentNullException("otherProperty");
            }

            this.otherProperty = otherProperty;
        }

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

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            PropertyInfo otherPropertyInfo = validationContext.ObjectInstance.GetType().GetProperty(otherProperty);

            if (otherPropertyInfo == null)
            {
                return new ValidationResult(string.Format("Property '{0}' is undefined.", otherProperty));
            }

            var otherPropertyValue = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);

            if (otherPropertyValue == null && value == null)
            {
                return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
            }

            return ValidationResult.Success;
        }
        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            yield return new ModelClientValidationRule
            {
                ErrorMessage = FormatErrorMessage(metadata.DisplayName),
                //This is the name of the method aaded to the jQuery validator method (must be lower case)
                ValidationType = "oneoftworequired"
            };

        }
    }

Add this to the View or partialview, you must make sure this is not in a $(document).ready method

    jQuery.validator.addMethod("oneoftworequired", function (value, element, param) {
        if ($('#Phone).val() == '' && $('#Mobile).val() == '')
            return false;
        else
            return true;
    });

    jQuery.validator.unobtrusive.adapters.addBool("oneoftworequired");

the jQuery validator stuff only seems to be needed if you want to validate the form without posting back or on initial page load and to do that you just call $('form').valid()

Hope this helps somebody :)

like image 167
Troublesum Avatar answered Nov 15 '22 05:11

Troublesum