Registering Validators in Global.asax, is this necessary?

Feb 1, 2011 at 4:00 PM

Good morning everyone, maybe someone can tell me if we need to register the validators in the global.asax file for them to work.  I am using MVC 3 at the moment.

 

Thanks in advance.

 

Michael Reyeros

Coordinator
Feb 1, 2011 at 4:09 PM

Hi. The built in validators should just work, they are self registering.

If you are building custom validators, you will need to register those. I personally do this is the static constructor of the validator, as shown at the bottom of this page: http://foolproof.codeplex.com/. However, registering them in the global.asax should also work.

Feb 1, 2011 at 4:26 PM

OK yes, I have a custom validator that inherits from RequiredIf attribute.  But static constructors must be parameterless and RequiredIf requires parameters so I guess I will go the global.asax route.  Another question, my custom validator basically checks to see if the dependentproperty's value is equal to "xyz" then the property should be required.  Do I need to override the IsValid on the custom attribute or can I simply call the base.IsValid?

Feb 1, 2011 at 4:31 PM

Additionally if my viewmodel is structured like this:
ReservationData

AddressInfo <- ComplexType

StateRegion <- Property to be evaluated

Country <- Dependent Property

 

On the generated html the id of the control for StateRegion is set to "AddressInfo.StateRegion" and the one for Country is set to "Addressinfo.Country".  When declaring my attribute on the AddressInfo object how should I set the value of the DependentProperty parameter, simply "Country" or do I have to prefix it with "AddressInfo"? 

Coordinator
Feb 1, 2011 at 4:42 PM

No, you shouldn't have to prefix it. Foolproof doesn't handle prefixing anyhow (maybe it should). 

However, based on your description, it seems like the build in functionality of RequiredIf would work:

[RequiredIf("Country", "xyz")]
public string StateRegion { get; set; }

I'm not sure why you need a custom validator, maybe you didn't describe all of the details that the custom validator will be handing. 

Feb 1, 2011 at 4:53 PM

Well both of the controls that are bound to the StateRegion and Country are Dropdown lists.  Each list has a default option of "0" so what I want to validate is if the country value is United States, lets say then the state value must be something other than "0".

Coordinator
Feb 1, 2011 at 5:11 PM

I see. So you are really dependent on more than one property. For that, I suggest inheriting from ModelAwareValidationAttribute.

Here is a blog post I made on it:

http://www.nickriggs.com/posts/build-model-aware-custom-validation-attributes-in-asp-net-mvc-2/

Feb 1, 2011 at 6:04 PM

Ok I tried the suggestions from that blog post and I believe that they will work for my needs but I am not sure what I am doing wrong.  When I load my app and submit my form I do not get any validation messages for those controls.  I have also tried setting a break point on the IsValid call and it seems that it does not get hit.  I must be doing something wrong and it may have something to do with some javascript that I have implemented on my form.  The javascript is as follows:

$step.find("input").each(function () {
                // validate every input element in this step
                if (!validator.element(this)) {
                    anyError = true;
                }
            });
What I am thinking is that since these are dropdown lists or html select elements they are not getting called.  The reason that I have written this code is that I am implementing a wizard type functionality on my view that shows and hides divs on the page.  So when the user clicks the next button it should validate what is being displayed.  What are your thoughts?

Feb 1, 2011 at 6:11 PM

While stepping through my javascript I noticed that the form validator does not pick up the validators that are associated to the select elements.

Feb 1, 2011 at 6:37 PM

I have also tried setting a breakpoint in the Register.cs class on the call to All() and it seems that it is never hit.  I am wondering if I am missing something.

Coordinator
Feb 1, 2011 at 6:42 PM

Would it be possible to post your model and custom validator code? Or perhaps you can replicate the behavior in a small sample project and send that to me?

nicholasriggs@gmail.com

Feb 1, 2011 at 7:13 PM

This is my validator:

public class StateRequiredIfAttribute : ModelAwareValidationAttribute
    {
        static StateRequiredIfAttribute()
        {
            Register.Attribute(typeof(StateRequiredIfAttribute));
        }

        public override bool IsValid(object value, object container)
        {
            var address = container as AddressInfo;
            if (!string.IsNullOrEmpty(address.Country) &&
                address.Country == "United States")
                if (string.IsNullOrEmpty(value.ToString()))
                    return false;
            return true;
        }
    }
 This is my model object:
 
public class AddressInfo
    {
        [Display(Name="AddressInfo_Address1", ResourceType=typeof(UIDisplay))]
        [Required(ErrorMessageResourceName = "AddressInfo_Address1Required", ErrorMessageResourceType = typeof(UIValidation))]
        public virtual string Address1 { get; set; }

        [Display(Name = "AddressInfo_Address2", ResourceType = typeof(UIDisplay))]
        public virtual string Address2 { get; set; }

        [Display(Name = "AddressInfo_City", ResourceType = typeof(UIDisplay))]
        [Required(ErrorMessageResourceName = "AddressInfo_CityRequired", ErrorMessageResourceType = typeof(UIValidation))]
        public virtual string City { get; set; }

        [Display(Name = "AddressInfo_StateRegion", ResourceType = typeof(UIDisplay))]
        [StateRequiredIf(ErrorMessageResourceName = "AddressInfo_CountryStateRequired", ErrorMessageResourceType = typeof(UIValidation))]
        public virtual string StateRegion { get; set; }

        [Display(Name = "AddressInfo_PostalCode", ResourceType = typeof(UIDisplay))]
        [Required(ErrorMessageResourceName = "AddressInfo_PostalCodeRequired", ErrorMessageResourceType = typeof(UIValidation))]
        public virtual string PostalCode { get; set; }

        [Display(Name = "AddressInfo_Country", ResourceType = typeof(UIDisplay))]
        [Required(ErrorMessageResourceName = "AddressInfo_CountryRequired", ErrorMessageResourceType = typeof(UIValidation))]
        public virtual string Country { get; set; } 

And in my View I am calling the validationmessagefor as usual
@Html.DropDownListFor(m => m.Address.StateRegion, Html.EmptySelectList())
                @Html.LabelFor(m=>m.Address.StateRegion)
                @Html.ValidationMessageFor(m=>m.Address.StateRegion)
 

Coordinator
Feb 1, 2011 at 8:14 PM
Edited Feb 1, 2011 at 8:14 PM

I constructed a working MVC 3.0 example based on what you gave me. I stripped it down for the sample however:

http://dl.dropbox.com/u/73019/FoolproofSample.zip

By the way, the following would throw an exception if value was null, so I changed it:

 

if (string.IsNullOrEmpty(value.ToString()))

 

 

Feb 1, 2011 at 8:25 PM

ok so do you think the issue then is the way that I am calling validate on the form element through my jquery?

var validator = $("form").validate();
In validator I only see the validators for the textboxes that are on the form and do not see the ones for the dropdown lists.  Since I am trying to create a wizard type functionality I do not want them to "submit" the form until all steps are completed and all input is valid.

Coordinator
Feb 1, 2011 at 9:00 PM

Probably.

Two things:

1) You will need to create a client side validator for your custom validator. $("form").validate() will only execute client side validation, not server side.

2) You might consider creating a <form> for each of your wizard pages so that you can executing validation for one page at a time. But if you do that, you will have to assembly a form with all the values at the end of your wizard when you actually want to submit. 

Feb 1, 2011 at 9:02 PM

First off thank you for all of your help today, I really do appreciate it. :)

Would this be the correct way of declaring the client side validator:

 jQuery.validator.addMethod("staterequiredif", function (value, element, params) {
    var dependentProperty = cwr.getId(context.fieldContext.elements[0], "Country");
    var dependentValue = document.getElementById(dependentProperty).value;

    if (dependentValue == "United States")
        if (dependentValue != "")
            return true;
        else
            return context.validation.fieldErrorMessage;

    return true;
});
 

Feb 2, 2011 at 1:07 AM

Ok so I have tried creating a custom validator on a text field and just to test it out I have tried declaring the client validator as follows in the foolproof.unobtrusive.js file:

jQuery.validator.addMethod("mobilephonerequiredif", function(value, element, params) {
        return false;
    });

 

 I set it to return false always to see if this would even work but I still do not see any validation messages.  In my global.asax file I have called

Register.Attribute(typeof(MobilePhoneRequiredIfAttribute));

to ensure that it gets registered because without it my code would never hit the static constructor in the attribute class.  

Here is what the attribute looks like

public class MobilePhoneRequiredIfAttribute : ModelAwareValidationAttribute
    {
        static MobilePhoneRequiredIfAttribute() { Register.Attribute(typeof(MobilePhoneRequiredIfAttribute)); }
        public override bool IsValid(object value, object container)
        {
            var model = container as ContactInfo;
            if (string.IsNullOrEmpty(model.HomePhone) && string.IsNullOrEmpty(model.WorkPhone) 
                && (value == null || string.IsNullOrEmpty(value.ToString())))
                return false;
            return true;
        }
    }

Am I missing something on the client side validation setup that could be causing this?  I was able to test with an attribute of type RequiredIf and it worked fine. 

 

 

Coordinator
Feb 2, 2011 at 12:15 PM

It you look further down the unobtrusive.js file you will see this pattern:

    var setValidationValues = function (options, ruleName, value) {
        options.rules[ruleName] = value;
        if (options.message) {
            options.messages[ruleName] = options.message;
        }
    };

    var $Unob = $.validator.unobtrusive;

    $Unob.adapters.add("requiredif", ["dependentproperty", "dependentvalue", "operator", "pattern"], function (options) {
        var value = {
            dependentproperty: options.params.dependentproperty,
            dependentvalue: options.params.dependentvalue,
            operator: options.params.operator,
            pattern: options.params.pattern
        };
        setValidationValues(options, "requiredif", value);
    });
You will need to do something similar for your custom js

Feb 2, 2011 at 5:43 PM

Thank you Nick, you have been an excellent help.  I found that last night while going over the code and figured out that I was missing that portion of the client validation setup.  If I run into anything else I will let you know.