0

I am using normal mvc textboxfor on strong view, I have created custom validation attribute (the detailed code explained below).

On form submit everything works fine. In case if validation fails by natural it shows error message as configured.

Now when I enter the correct value inside text box I expect the error message to be vanished automatically but this does not happen until I post the form

JS File

$.validator.addMethod('validaterequiredif', function (value, element, parameters) {
    var id = parameters['dependentproperty'];
    var clickValue = $("input[name=" + id + "]:checked").val();
    // get the target value (as a string, 
    // as that's what actual value will be)
    var targetvalue = parameters['targetvalue'];

    if (clickValue == targetvalue) {
        if (value == null) {
            return false;
        }
        else {
            return $.validator.methods.required.call(
       this, value, element, parameters);
        }
    }
    else {
        return true;
    }
});

$.validator.unobtrusive.adapters.add(
    'validaterequiredif',
    ['dependentproperty', 'targetvalue'],
    function (options) {
        options.rules['validaterequiredif'] = {
            dependentproperty: options.params['dependentproperty'],
            targetvalue: options.params['targetvalue']
        };
        options.messages['validaterequiredif'] = options.message;
    });

Server side custom validator class as below

public class ValidateRequiredIf : ValidationAttribute, IClientValidatable
    {
        protected RequiredAttribute _innerAttribute;

        public string DependentProperty { get; set; }
        public object TargetValue { get; set; }

        public bool AllowEmptyStrings
        {
            get
            {
                return _innerAttribute.AllowEmptyStrings;
            }
            set
            {
                _innerAttribute.AllowEmptyStrings = value;
            }
        }

        public ValidateRequiredIf(string dependentProperty, object targetValue)
        {
            _innerAttribute = new RequiredAttribute();
            DependentProperty = dependentProperty;
            TargetValue = targetValue;
        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            // get a reference to the property this validation depends upon
            var containerType = validationContext.ObjectInstance.GetType();
            var field = containerType.GetProperty(DependentProperty);

            if (field != null)
            {
                // get the value of the dependent property
                var dependentValue = field.GetValue(validationContext.ObjectInstance, null);
                // trim spaces of dependent value
                if (dependentValue != null && dependentValue is string)
                {
                    dependentValue = (dependentValue as string).Trim();

                    if (!AllowEmptyStrings && (dependentValue as string).Length == 0)
                    {
                        dependentValue = null;
                    }
                }

                // compare the value against the target value
                if ((dependentValue == null && TargetValue == null) ||
                    (dependentValue != null && (TargetValue.Equals("*") || dependentValue.Equals(TargetValue))))
                {
                    // match => means we should try validating this field
                    //if (!_innerAttribute.IsValid(value))
                    if (value == null)
                        // validation failed - return an error
                        return new ValidationResult(FormatErrorMessage(validationContext.DisplayName), new[] { validationContext.MemberName });
                }
            }

            return ValidationResult.Success;
        }


        private string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext)
        {
            // build the ID of the property
            string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(DependentProperty);
            // unfortunately this will have the name of the current field appended to the beginning,
            // because the TemplateInfo's context has had this fieldname appended to it. Instead, we
            // want to get the context as though it was one level higher (i.e. outside the current property,
            // which is the containing object, and hence the same level as the dependent property.
            var thisField = metadata.PropertyName + "_";
            if (depProp.StartsWith(thisField))
                // strip it off again
                depProp = depProp.Substring(thisField.Length);
            return depProp;
        }

        public virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule
            {
                ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
                ValidationType = "validaterequiredif",
            };

            string depProp = BuildDependentPropertyId(metadata, context as ViewContext);

            // find the value on the control we depend on;
            // if it's a bool, format it javascript style 
            // (the default is True or False!)
            string targetValue = (TargetValue ?? "").ToString();
            if (TargetValue is bool)
                targetValue = targetValue.ToLower();

            rule.ValidationParameters.Add("dependentproperty", depProp);
            rule.ValidationParameters.Add("targetvalue", targetValue);

            yield return rule;
        }
    }

Model property

[ValidateRequiredIf("IsFeederSelected", "True", ErrorMessage = "Please select atleast one feeder")]
 public List<string> selectedMeterName { get; set; }

Strongly typed view

 <div class="meterTextboxRadio col-md-4">
        <p>
            @Html.RadioButtonFor(m => m.IsFeederSelected, true, new { @class = "radio", @Name = "IsFeederSelected", value = "meter", id = "rdbMeterConsumption", @checked = "checked" })
            <span> Feeder</span>
            @Html.RadioButtonFor(m => m.IsFeederSelected, false, new { @class = "radio", @Name = "IsFeederSelected", value = "group", id = "rdbGroupConsumption",style= "margin-left: 30px;" })
            <span> Group</span>
        </p>
        <div class="group dropdownhidden" id="MeterNameConsumption" style="margin-top:4px;">
            @Html.DropDownListFor(m => m.selectedMeterName, Model.MeterName
                , new { @class = "chosen-select" ,@id= "ddlConsumptionMeterName", multiple = "multiple", Style = "width:100%", data_placeholder = "Choose Feeders.." })
            <span class="highlight"></span> <span class="bar"></span>
        </div>
        <div class="group dropdownhidden" id="GroupNameConsumption" style="margin-top:4px;">
            @Html.DropDownListFor(m => m.selectedGroupName, Model.GroupName
                , new { @class = "chosen-select", @id = "ddlConsumptionGroupName", multiple = "multiple", Style = "width:100%", data_placeholder = "Choose Group.." })
            <span class="highlight"></span> <span class="bar"></span>
        </div>
        @Html.ValidationMessageFor(m => m.selectedMeterName, "", new { @class = "error" })
        @Html.ValidationMessageFor(m => m.selectedGroupName, "", new { @class = "error" })
    </div>

Please provide some inputs for the same

Thanks.

daredevil
  • 127
  • 2
  • 15
  • How do you expect us to fix your code when you do not even show it! –  Nov 27 '17 at 06:25
  • What do you mean _on focus change_? By default validation is triggered on the `.blur()` event (if the value has changed), and thereafter on `.keyup()` –  Nov 27 '17 at 06:54
  • @StephenMuecke thanks for reply. My client side custom validation not call on .blur() event ,its call on form submit. But the [Required], [RegularExpression("^[ A-Za-z0-9_@./#&+-]*$", ErrorMessage = "Please enter alphanumeric charecters only")] this call on blur() event. – daredevil Nov 27 '17 at 07:16
  • What is this being applied to, and what is your server side `ValidationAttribute`? You have some odd code such as `var clickValue = $("input[name=" + id + "]:checked").val();` and `return $.validator.methods.required.call(..)` that makes it a bit hard to understand what your actually validating. –  Nov 27 '17 at 07:20
  • Its normal mvc textboxfor on strongly typed view, I have created custom validation attribute (The part of code is shown above). On form submit everything works fine. In case if validation fails by natural it shows error message as configured. Now after that when I enter the correct value inside text box I expect the error message to be vanished automatically but this does not happen until I post the form. – daredevil Nov 27 '17 at 07:40
  • No, all you have shown is the scripts. You have not shown any code associated with your attribute. And as I noted, you have some odd code which makes it difficult to understand what your doing –  Nov 27 '17 at 07:42
  • @StephenMuecke Pl have a look at edited question, hope that will help – daredevil Nov 27 '17 at 09:28
  • Now you have shown even more strange code, particularly your `protected RequiredAttribute _innerAttribute;`. And then there is the really bad code in your view - the `new { Name = ".." }` and `new { checked = "checked" }` which you need to remove (your **never** set those attributes) –  Nov 27 '17 at 09:40
  • I suggest you read [The Complete Guide To Validation In ASP.NET MVC 3 - Part 2](https://www.devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net-mvc-3-part-2), and for a working example of a `[RequiredIf]` attribute, refer [this project](https://github.com/stephenmuecke/mvc-conditionalvalidation) –  Nov 27 '17 at 09:40
  • And then there is the fact that your dropdownlist will never bind correctly. To create a ` –  Nov 27 '17 at 09:46

0 Answers0