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.