How to write custom client-side jQuery validation in ASP.NET MVC 2 RC?

I've read Phil Haack's post on custom client-side validation in ASP.NET MVC 2. I want to do the same thing but with the jQuery adapter and using ASP.NET MVC 2 RC (as opposed to MVC 2 Beta that the post uses). Has anyone been able to figure how to do this?

I specially want to implement the password matching validation (i.e. password & confirm password must match). The ASP.NET MVC 2 RC VS.NET project template does show how to implement that on the server-side (using the PropertiesMustMatchAttribute) but not on the client-side.

Answers


I assume you already followed Phil Haack's instructions here http://haacked.com/archive/2009/11/19/aspnetmvc2-custom-validation.aspx) on how to get custom validation working with MS AJAX client validation. To get it to work with jQuery, you'll need to modify the MicrosoftMvcJQueryValidation.js file:

  • In the __MVC_CreateRulesForField(validationField) function, you'll need to add a case statement. Continuing Phil's example, you'll need to add:

    case "price":

    __MVC_ApplyValidator_Price(rulesObj, thisRule.ValidationParameters["min"]);

    break;

  • You'll then need to create the __MVC_ApplyValidator_Price function:

function __MVC_ApplyValidator_Price(object, value) {

// min is what jQuery Validate uses to validate for minimum values
object["min"] = value;

}

That should be enough to get Phil's example working.

Now, regarding your PropertiesMustMatchAttribute validation, it doesn't look like MVC generates the client-side json validation definition for attributes that decorate classes. Since PropertiesMustMatchAttribute must be used on the model (and not the property), I can't figure out how to make it trigger client-side validation. Instead, I took a different approach. I created a dummy validation attribute who's IsValid() overload always returns true, and used this attribute on a property. This is just a dummy attribute that will delegate the validation logic to jQuery validator's equalTo function. Here's the dummy attribute:

public class PropertiesMustMatchClientTriggerAttribute : ValidationAttribute
{
    public string MatchProperty { get; set; }

    public PropertiesMustMatchClientTriggerAttribute(string matchProperty)
    {
        MatchProperty = matchProperty;
        ErrorMessage = "{0} doesn't match {1}.";
    }
    public override bool IsValid(object value)
    {
        return true;
    }

    public override string FormatErrorMessage(string name)
    {
        return String.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, MatchProperty);
    }
}

Here is the custom validator:

public class PropertiesMustMatchClientTriggerValidator : DataAnnotationsModelValidator<PropertiesMustMatchClientTriggerAttribute>
{
    private string _message;
    private string _matchProperty;

    public PropertiesMustMatchClientTriggerValidator(ModelMetadata metaData, ControllerContext context, PropertiesMustMatchClientTriggerAttribute attribute)
        : base(metaData, context, attribute)
    {
        _message = attribute.FormatErrorMessage(metaData.DisplayName);
        _matchProperty = attribute.MatchProperty;
    }

    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
    {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = _message,
            ValidationType = "equalTo"
        };
        rule.ValidationParameters.Add("matchField", _matchProperty);

        return new[] { rule };
    }
}

the above custom validator needs to be registered in Application_Start() per Phil's blog:

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(PropertiesMustMatchClientTriggerAttribute), typeof(PropertiesMustMatchClientTriggerValidator));

Finally, you need to modify the MicrosoftMvcJQueryValidation.js file:

  • Add the following case statement to __MVC_CreateRulesForField:

case "equalTo":

__MVC_ApplyValidator_EqualTo(rulesObj, thisRule.ValidationParameters["matchField"]);

break;

  • add this function:

function __MVC_ApplyValidator_EqualTo(object, elemId) {

object["equalTo"] = document.getElementById(elemId);

}

Now you need to attach the dummy validation attribute to a property:

    [PropertiesMustMatchClientTrigger("Password")]
    public string ConfirmPassword { get; set; }

That should do it.

Creating this dummy attribute is a bit ugly, so I hope someone can come up with a more elegant solution.


Need Your Help

Is there a way to map a value in an object to the index of an array in javascript?

javascript jquery arrays performance knockout.js

Prepending that a solution only needs to work in the latest versions of Chrome, Firefox, and Safari as a bonus.

Can't copy sqlite database from app's mainbundle to users documents

iphone ios database sqlite ios-simulator

I can't copy my sqlite database to the users documents folder, the database has always 0 byte but should have 322 kb.