DataAnnotationsValidator Enabled property

Nov 26, 2012 at 9:16 AM

Hi, just a quick one, if the enabled property is set to false should the control render any contents??

Coordinator
Jan 5, 2013 at 2:23 PM

Yes, it will render contents. This is the default behaviour for standard validation controls. This gives ability dynamically enable/disable validators from JavaScript.

May 27, 2014 at 8:31 PM
How can I dynamically enable/disable validators from JavaScript?

I have tried to set 'data-val=false' but it did not work.

Thanks
Coordinator
May 28, 2014 at 9:16 AM
Edited May 28, 2014 at 9:19 AM
The short answer
Unobtrusive validation in Web Forms is not the same as in MVC. It just looks and feels the same, but underneath there is still the same "good and old" validation library. So as always you need to use enabled property of the validator object which is stored in Page_Validators array. You can iterate through this array and based on controltovalidate (or whatever other condition) you can find your validator and then disable it on the client side. But still this will not disable server-side validation.
And here is more expanded explanation of why changing data-val to false will not work:
data-val attribute is used only once when MS validation library script parses document and fills Page_Validators array. If you will take a look at this script you will notice that code at the very bottom:
function parseSpecificAttribute(selector, attribute, validatorsArray) {
    return $(selector).find("[" + attribute + "='true']").each(function (index, element) {
        addValidationExpando(element);
        element.dispose = function () { dispose(element); element.dispose = null; };
        if ($.inArray(element, validatorsArray) === -1) {
            validatorsArray.push(element);
        }
    }).length;
}
As I wrote before this function is called with parameters document, data-val and Page_Validators only once when document is parsed. This all means that data-val=false will work only if you will send it from server with the first request or if you will update document partially through the UpdatePanel. Whenever UpdatePanel.pageLoaded function is called the contents will be reparsed.

Also there is no possibility to trigger reparsing of the document manually since parse function is not exposed to the outside. Surely you can copy and paste this function and call it whenever you need, but for me this solution is too dirty.
May 31, 2014 at 1:34 PM
Thanks for your answer amanek.

I have looked this in more detail and I think I found a solution.
My goal was not to disable all the validation, but only disable validation for controls that are hidden or disabled.
I have modified the Datavalidation code so it does not validate hidden/disabled controls both on client and serverside.

//DataAnnotationsValidator.cs
protected override bool EvaluateIsValid()
        {
          ********************* CHANGE ***************
           var control = (WebControl)FindControl(ControlToValidate);
            if (control.Visible && control.Enabled)
            {
          ********************* END CHANGE ***************
                object value = GetControlValidationValue(ControlToValidate);
                foreach (ValidationAttribute validationAttribute in ValidationAttributes)
                {
                    // Here, we will try to convert value to type specified on RangeAttibute. 
                    // RangeAttribute.OperandType should be either IConvertible or of built in primitive types
                    var rangeAttibute = validationAttribute as RangeAttribute;
                    if (rangeAttibute != null)
                    {
                        value = Convert.ChangeType(value, rangeAttibute.OperandType, CultureInfo.CurrentCulture);
                    }

                    if (validationAttribute.IsValid(value)) continue;

                    ErrorMessage = validationAttribute.FormatErrorMessage(DisplayName);
                    return false;
                }
            }

            return true;
        }
DAValidation.NET45.js
DataAnnotationsValidatorIsValid: function (val) {
                var functionsToEvaluate = val.validatorfunctions.split(';;');
                var errorMessages = val.errormessages.split(';;');
                var element = document.getElementById(val.controltovalidate);
                      ********************* CHANGE ***************
               if (!element.disabled && // not disabled
                    !element.hidden && // not hidden
                    (element.offsetWidth > 0 || element.offsetHeight > 0)) // not hidden by a parent
                {
                     ********************* END CHANGE ***************
                    for (var funcIndex in functionsToEvaluate) {
                        var result = eval(functionsToEvaluate[funcIndex] + "(val)");
                        if (result === false) {
                            val.errormessage = errorMessages[funcIndex];
                            if (val.supresserrormessagetext === "false") {
                                val.innerHTML = errorMessages[funcIndex];
                            }
                            return false;
                        }
                    }
                }
                return true;
            },
Above are the changes I made.
So now I can hide/disable a control on client side and the clientside validation will not fire.

Note that hide/disable a control on clientside will not remove the serverside validation, in order to do this, the control need to be disabled/hidden on server side as well:
protected void mybutton_OnClick(object sender, EventArgs e)
        {
            if (// some logic to determine if control was disabled on clientside)
            {
                myControl.Enabled = false;
            }
           
            // Validate the page again
            Page.Validate();

            if (Page.IsValid)
            {
                
            }
       }
Coordinator
Jun 3, 2014 at 9:30 AM
Edited Jun 3, 2014 at 9:37 AM
While I see that it effectively solves your task I cannot agree with you on this change applied globally to the project. The change introduces behavior which is not standard for existing ASP.NET validation controls. For instance, the check if (control.Visible && control.Enabled) on server side is not natural, because current implementation of Web Forms validation flow is controlled by Enabled and Visible properties of the validator instance on server side and by enabled field on JavaScript object on client side. For proof lets refer to the ASP.NET sources:

On server side:
  1. Page.Validate calls Validate method on all validator controls. As you can see validators collection is of type IValidator, which is implemented only by BaseValidator class.
  2. The Basevalidator.Validate method takes into account Visible and Enabled properties of the validator control instance.
// Fragment from BaseValidator.Validate method
IsValid = true;
if (!Visible || !Enabled) {
    return;
}
On client side:
Function ValidatorValidate from standard library takes care only about enabled field of the validator JavaScript object. See code below:
function ValidatorValidate(val, validationGroup, event) {
    val.isvalid = true;
    if ((typeof(val.enabled) == "undefined" || val.enabled != false) && IsValidationGroupMatch(val, validationGroup)) {
        if (typeof(val.evaluationfunction) == "function") {
            val.isvalid = val.evaluationfunction(val);
            if (!val.isvalid && Page_InvalidControlToBeFocused == null &&
                typeof(val.focusOnError) == "string" && val.focusOnError == "t") {
                ValidatorSetFocus(val, event);
            }
        }
    }
    ValidatorUpdateDisplay(val);
}
As you can see validation in ASP.NET is supposed to be controlled independently from the input controls. On server side we have Visible and Enabled properties and on client side we have ValidatorEnable function.