I have created a custom group validator that ensure that the user has filled in at least one field from a given group. This works when I load the page and submit the form without entering anything.
Invalid Form
Valid Form
However, the issue is whenever the form has any error other than the group validation error. When I force the form to display the group required validation error and then display, for example, a string length error on one of the fields in the group the group validation error doesn't disappear.
After a bit of debugging I'm fairly certain that its the client side validation that is not working correctly as its not removing the server side validation error whenever a client side validation is generated. When the form generates the string length error it prevents the form from being posted to the server which would remove the group validation required error.
Can anyone see an issue with the client side validation code? I copied the code from this stackoverflow page MVC3 unobtrusive validation group of inputs and and changed it to meet my needs. This is the first time I have attempted custom validation so forgive me if the issue is something really simple. Another indicator is that whenever I enter and then remove a single character the group validation error doesn't turn on/off.
I have provided the custom validator code below:
Custom Validator C# (Serverside and Clientside)
public class RequireAtLeastOneIfAttribute : ValidationAttribute, IClientValidatable
{
private string[] Properties { get; set; }
public RequireAtLeastOneIfAttribute(params string[] properties)
{
Properties = properties;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if(Properties == null || Properties.Length < 1)
{
return null;
}
foreach (var property in Properties)
{
var propertyInfo = validationContext.ObjectType.GetProperty(property);
var propertyValue = propertyInfo.GetValue(validationContext.ObjectInstance, null);
if (propertyInfo == null)
{
return new ValidationResult(string.Format("Unknown property {0}", property));
}
if (propertyValue is string & !string.IsNullOrEmpty(propertyValue as string))
{
return null;
}
if (propertyValue != null)
{
return null;
}
}
return new ValidationResult("At least one age group field is required");
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = ErrorMessage,
ValidationType = "requireatleaseoneif"
};
rule.ValidationParameters["properties"] = string.Join(",", Properties);
yield return rule;
}
}
Javascript
The only changes I made to the javascript is the id.
jQuery.validator.unobtrusive.adapters.add(
'requireatleaseoneif', ['properties'], function (options) {
options.rules['requireatleaseoneif'] = options.params;
options.messages['requireatleaseoneif'] = options.message;
}
);
jQuery.validator.addMethod('requireatleaseoneif', function (value, element, params) {
var properties = params.properties.split(',');
var values = $.map(properties, function (property, index) {
var val = $('#' + property).val();
return val != '' ? val : null;
});
return values.length > 0;
}, '');
Razor
@Html.RadioButtonFor(m => m.HaveDependants, true, new { id = "borrower-haveDependants-true", @class = "block-radio-input" })
@Html.RadioButtonFor(m => m.HaveDependants, false, new { id = "borrower-haveDependants-false", @class = "block-radio-input" })
@Html.ValidationMessageFor(m => m.HaveDependants)
@Html.LabelFor(m => m.DependantsAgeGroupOne)
@Html.TextBoxFor(m => m.DependantsAgeGroupOne, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.DependantsAgeGroupOne)
@Html.LabelFor(m => m.DependantsAgeGroupTwo)
@Html.TextBoxFor(m => m.DependantsAgeGroupTwo, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.DependantsAgeGroupTwo)
@Html.LabelFor(m => m.DependantsAgeGroupThree)
@Html.TextBoxFor(m => m.DependantsAgeGroupThree, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.DependantsAgeGroupThree)
I also that the issue might be with how its triggered. Because the validation attribute isn't on the text input themselves but rather on the radio button I added the following javascript to validate on the whole form on button click.
<script>
$(document).ready(function () {
$(form).validate;
});
</script>
I changed the last bit of Javascript to the following
$('input').blur(function () {
$('.haveDependants').valid();
})