0

I am attempting to use a custom validation attribute to ensure a checkbox ends up checked. However, the validation error is displaying (not styled) as part of the checkbox's label prior to the validation firing.

To detail:

I have the following property and attribute:

[Display(Name = "TermsAndConditions", ResourceType = typeof(Res.Labels))]
[MustBeTrue(ErrorMessageResourceName = "TermsAndConditionsValidationMessage", ErrorMessageResourceType = typeof(Res.Labels))]
public bool TermsAndConditions { get; set; } = true;

(for whatever reasons. defaulting to true is deliberate. It's swapped back to false on the view):

@model MyModel
@{ 
        MyModel.TermsAndConditions = false;
}

The custom attribute looks like this:

public class MustBeTrueAttribute : ValidationAttribute, IClientValidatable
    {
        public override bool IsValid(object value)
        {
            return value is bool && (bool)value;
        }

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            yield return new ModelClientValidationRule
            {       
                ErrorMessage = FormatErrorMessage(Labels.TermsAndConditions),
                ValidationType = "mustbetrue"
            };
        }
    }

The HTML in the view looks like this:

<div class="checkbox">
    @Html.CheckBoxFor(m => m.TermsAndConditions, true)
    <div class="state">
        <label for="terms">
            @Html.Raw(Label.TermsAndConditions)
            @Html.ValidationMessageFor(m => m.TermsAndConditions, Label.TermsAndConditionsValidationMessage)
        </label>
    </div>
</div>

HTML.Raw is used as I need the label to render an anchor element, I couldn't find another way to do it.

The generated HTML on page load is this:

<div class="checkbox">

    <input data-msg-mustbetrue="You must accept the terms and conditions" data-msg-required="The I aceept the <a href=&quot;#&quot;>terms and conditions</a>. field is required." data-rule-mustbetrue="true" data-rule-required="true" id="TermsAndConditions" name="TermsAndConditions" type="checkbox" value="true">
    <input name="TermsAndConditions" type="hidden" value="false">
    <div class="state">
        <label for="terms">
            I aceept the <a href="#">terms and conditions</a>.
            <span class="field-validation-valid" data-valmsg-for="TermsAndConditions" data-valmsg-replace="false">You must accept the terms and conditions</span>
        </label>
    </div>
</div>

termsAndConditionsPreValidation

After firing the validation, everything works as expected:

<div class="checkbox">
    <input data-msg-mustbetrue="You must accept the terms and conditions" data-msg-required="The I aceept the <a href=&quot;#&quot;>terms and conditions</a>. field is required." data-rule-mustbetrue="true" data-rule-required="true" id="TermsAndConditions" name="TermsAndConditions" type="checkbox" value="true" class="input-validation-error">
    <input name="TermsAndConditions" type="hidden" value="false">
    <div class="state">
        <label for="terms">
            I aceept the <a href="#">terms and conditions</a>.
            <span class="field-validation-error" data-valmsg-for="TermsAndConditions" data-valmsg-replace="false">You must accept the terms and conditions</span>
        </label>
    </div>
</div>

termsAndCondtionsAfterValidation

I've been banging my head against the wall on and off with this stuff for a while now, can anyone point me in the right direction? Please and thanks.

  • you can set: [DefaultValue(false)] on the property to set the default value to avoid setting it false in view. – Nomi Ali Mar 19 '19 at 09:05
  • or remove =true from TermsAndConditions { get; set; } = true – Nomi Ali Mar 19 '19 at 09:11
  • Thanks @NomiAli, the ViewModel is used prior to the user actually seeing the view so Page.IsValid returns false unless it's set to true, hence why it's done the way I'm doing it. – monkeySeeMonkeyDo Mar 19 '19 at 09:20

1 Answers1

1

I have finally worked this out after banging my head against the wall.

This particular instance is due to missing CSS. It's normal the error is on the page, it should just be hidden to begin with. This is the only place a custom attribute is being used so it never occurred to me it'd be the CSS as all other validation is on point.

span.field-validation-valid {
        display: none;
}

and voila, sorted.