Multi-lingual form validation using MVC DataAnnotation attributes and Sitecore – Part 3 (Custom validation, soup to nuts)

So you wanna provide a custom validation data annotation that would function in both the server and client validation arena?

international

Below we have a scenario where there are two form fields highlighted, Password and VerifyPassword. Imagine that we would like to make the VerifyPassword property required IF the Password field was filled out. [This could exist, for example, on a ‘change password’ form.]

The DataAnnotation provided within ASP.NET MVC don’t have a built-in attribute to support this so we will build one based on the Required attribute.

1) Create a custom attribute class that inherits from RequiredAttribute and IClientValidatable. If either of these concepts are new, please see Part 1 and Part 2 of the series.

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
    public class RequiredIfPopulated : RequiredAttribute, IClientValidatable
    {
        public  string PropertyName { get; set; }

        public RequiredIfPopulated(string propertyName)
        {
            PropertyName = propertyName;
        }

        protected override ValidationResult IsValid(object value, ValidationContext context)
        {
            Object instance = context.ObjectInstance;
            Type type = instance.GetType();
            Object propertyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
            if (propertyvalue != null)
            {
                ValidationResult result = base.IsValid(value, context);
                return result;
            }
            return ValidationResult.Success;
        }

        public override string FormatErrorMessage(string name)
        {
            return Translate.Text(base.FormatErrorMessage(name));
        }

        public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var clientValidationRule = new ModelClientValidationRule()
            {
                ErrorMessage = FormatErrorMessage(ErrorMessage),
                ValidationType = "requiredif"
            };

            clientValidationRule.ValidationParameters.Add("other", OtherProperty));

            return new[] { clientValidationRule };
        }
    }

Something new added to the class is the IsValid override. This gets the value of the parameter, PropertyName (in this case, Password), and validates that, if it is populated, that the source property (i.e. VerifyPassword) is also populated. If it is not, the validation fails on the server side.

The GetClientValidationRules method is similar to Part 2‘s CompareTranslated custom attribute. The primary difference is that the “ValidationType” is defined as ‘requiredif’ — a validation rule not available in jQuery.validate.js. You can name this new ValidationType anything javascript-acceptable (i.e. no script keywords), but it must be 100% lowercase.

2) Decorate the source property (VerifyPassword) with the new custom attribute.

[MinLengthTranslated(6, ErrorMessage = "PasswordMinLength")]
public string Password {get; set;}

[RequiredIfPopulated("Password", ErrorMessage = "VerifyPasswordRequired")]
[CompareTranslated("Password", ErrorMessage = "VerifyPasswordInvalid")]
public string VerifyPassword {get; set;}

3) Now, for the javascript.

$.validator.addMethod(
    "requiredif",
    function (val, element, other) {
        var target = $(other);
        return target.val().length == 0 || $.trim(val).length > 0 || (target.val().length > 0 && $.trim(val).length > 0);
    });

$.validator.unobtrusive.adapters.add('requiredif', ['other'],
    function (options) {
        options.rules["requiredif"] = options.params;
        options.messages["requiredif"] = options.message;
    });

$(".myform").data('validator', null);
$.validator.unobtrusive.parse($(".myform"));

First, we have to add a new function to jQuery.validator called ‘requiredif’ (exactly the same name as the ValidationType declared in GetClientValidationRules()). This function is added to the validation list with $.validator.addMethod and contains validation logic in javascript. The first param of the function is the value of the field being evaluated (VerifyPassword). The second is the VerifyPassword element, and the third is the ‘other’ parameter, in this case, Password.

Second, we have to add an adapter to $.validator.unobstrusive.adapters, using the ‘add; function, passing in: a) the name of the validator; b) is the ‘other’ parameter, in this case, Password. … remember the param “other” we declared in GetClientValidationRules()? Here’s one place we are using it. c) Finally, the method ties the rule and message for the attribute into the unobtrusive validator.

And last but not least, we have two very important lines of code. Your page and custom client validation may run just fine without it. It all depends on how you load the previous two snippets of javascript. Here’s the trick … they need to be loaded after the 3 jquery includes (defined in Part 2) but prior to DOM ready and certainly NOT in (document).ready.

These two lines of code first, remove the previous set of validators, and then reload them. In effect, they have a chance to grab all the ones that were missed the first time around.

Stay tuned for future posts on fluent validation and perhaps some validator troubleshooting.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s