Marques
Marques

Reputation: 1914

ASP.NET MVC: Implement client side validation with attribute without IClientValidatable

How can I create a custom validation attribute with client side validation without implementing IClientValidatable?

How does System.ComponentModel.DataAnnotations.RequiredAttribute client side validate?

The reason to do this is because I'm using objects from classes in another project as models in my views and I don't want to add the System.Web.MVC reference to that project.

EDIT to add more information:

What I don't know is how to use the information from the custom validation attribute to add the necessary attributes to the HTML for unobtrusive validation to work.

This is my custom validation attribute:

public class RequiredGuidAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        Guid? guidValue = value as Guid?;

        if (guidValue == null)
            return false;

        return guidValue != Guid.Empty;
    }
}

This is my property with the attribute applied:

    [RequiredGuid(ErrorMessageResourceType = typeof(ClientOrderResources), ErrorMessageResourceName = "RequiredShippingMethod")]
    public Guid ShippingMethodId
    {
        get { return GetProperty(ShippingMethodIdProperty); }
        set { SetProperty(ShippingMethodIdProperty, value); }
    }

And finally I'm rendering a hidden input for that property in the view using Html.HiddenFor.

Now, how can I get the error message from the attribute to apply it to the HTML? Should I do it my self using Reflection or there is a better way?

And then how can I tell Html.HiddenFor to use that information to add the necessary attributes to the HTML?

Upvotes: 5

Views: 6626

Answers (3)

Zack Huber
Zack Huber

Reputation: 337

We had a similar problem. We have a model we use for our account creation that uses IClientValidatable on its custom attributes. However, we created a batch account creation process that sits outside of the website that we weren't able to reference System.Web.Mvc in. Because of this, when we called Validator.TryValidateObject, any custom validator that inherited from IClientValidatable was simply skipped. Here's what we were working with that was failing to validate outside of our website:

public class AgeValidatorAttribute : ValidationAttribute, IClientValidatable
{
    public int AgeMin { get; set; }
    public int AgeMax { get; set; }

    public override bool IsValid(object value)
    {
        //run validation
    }
}

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = ErrorMessageString,
            ValidationType = "agevalidator"
        };

        rule.ValidationParameters["agemin"] = AgeMin;
        rule.ValidationParameters["agemax"] = AgeMax;

        yield return rule;
    }

Removing System.Web.Mvc required us to also remove GetClientValidationRules and the IClientValidatable reference. In order to do this and still have client side validation, we had to create a new class:

public class AgeValidatorClientValidator : DataAnnotationsModelValidator<AgeValidatorAttribute>
{
    private readonly string _errorMessage;
    private readonly string _validationType;

    public AgeValidatorClientValidator(ModelMetadata metadata, ControllerContext context, AgeValidatorAttribute attribute)
        : base(metadata, context, attribute)
    {
        this._errorMessage = attribute.FormatErrorMessage(metadata.DisplayName);
        this._validationType = "agevalidator";
    }

    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
    {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = this._errorMessage,
            ValidationType = this._validationType
        };

        rule.ValidationParameters["agemin"] = base.Attribute.AgeMin;
        rule.ValidationParameters["agemax"] = base.Attribute.AgeMax;

        yield return rule;
    }
}

As you can see, it does essentially the same thing as it used to, it's just done using the DataAnnatotationsModelValidator rather than IClientValidatable. There's one more step we need to do to actually attach the DataAnnotationsModelValidator to the atttribute, and that's done in the Global.asax.cs Application_Start method

DataAnnotationsModelValidatorProvider.RegisterAdapter( typeof(AgeValidatorAttribute), typeof(AgeValidatorClientValidator));

Now you can use this just as you would use a normal attribute:

[AgeValidator(AgeMax = 110, AgeMin = 18, ErrorMessage = "The member must be between 18 and 110 years old")]
public string DateOfBirth { get; set; }

I know this question is a year old, but I spent all day yesterday and half of today trying to figure this issue out. So I hope this helps somebody who runs into the same problem if OP hasn't figured the answer out yet.

Please note, I did not include any javascript in this writeup as it required no changes from the standard implementation of custom validation rules using jQuery.validate.

Upvotes: 7

wertzui
wertzui

Reputation: 5730

It is possible, i found this article on how to do it: http://xhalent.wordpress.com/2011/05/12/custom-unobstrusive-jquery-validation-in-asp-net-mvc-3-using-dataannotationsmodelvalidatorprovider/

basically you have to create a DataAnnotationsModelValidator on your client an register it in Application_Start().

And don't forget that you still have to write the Javascript for client side validation.

Upvotes: 0

afr0
afr0

Reputation: 888

You can't have custom validation on the client unless you implement IClientValidatable. And for that you also need to add client script as well.

http://msdn.microsoft.com/en-us/vs2010trainingcourse_aspnetmvccustomvalidation_topic3.aspx

Upvotes: 0

Related Questions