I am trying to write a website using Razor pages in ASP.NET 7.0 and I am trying to get server side validation messages to display on the client using bootstrap 5.1 forms.
Bootstrap provides the class invalid-feedback
and valid-feedback
which are applied to the input field once the field is validated (after an unsuccessful post request). However to display the messages conditionally the input fields must be tagged with is-invalid
or is-valid
respectively. How do I get this information?
I know that I can get the validation message from the html helper using either the ValidationMessageTagHelper
or the IHtmlHelper
:
<label asp-for="Name" class="form-label"></label>
<input asp-for="Name" class="form-select @(IsValid ? "is-valid" : "is-invalid")" />
<div class="invalid-feedback">
<!-- <span>@Html.ValidationMessageFor(i => i.Name)</span> -->
<span asp-validation-for="Name"></span>
</div>
I want to make something similar to the second line where I apply the class to the input. However I cannot get the ValidationState from the Model, especially when all this logic is inside another EditorTemplate. So far I tried to get the Key that the property uses in the ModelStateDictionary using the same methods that ValidationMessageFor use to check that dictionary, but they are not publicly available in that class and I also tried to find methods in the HtmlHelper that give me access to that validation state.
So far I tried to use an extension method on the Html helper, but there is no ValidationState method without knowing the exact key, which is difficult in nested properties:
Html.ViewContext.ModelState.GetValidationState(key)
As in my comment I figured the NameFor
function does what I am looking for:
public static class HtmlExtensions {
/// <inheritdoc cref="ModelStateDictionary.GetValidationState(string)"/>
/// <param name="property">Property that provides the key.</param>
public static ModelValidationState GetValidationState<TModel, TPropertyType>(this IHtmlHelper<TModel> helper, Expression<Func<TModel, TPropertyType>> property)
=> helper.ViewContext.ModelState.GetValidationState(helper.NameFor(property));
public static string GetValidityClass<TModel, TPropertyType>(this IHtmlHelper<TModel> helper, Expression<Func<TModel, TPropertyType>> property)
=> helper.GetValidationState(property) switch {
ModelValidationState.Invalid => "is-invalid",
ModelValidationState.Valid => "is-valid",
_ => string.Empty,
};
}
Now I can use this extension to apply the class like this:
<div class="col-md-6">
<label asp-for="Name" class="form-label"></label>
<input asp-for="Name" class="form-control @Html.GetValidityClass(i => i.Name)" />
<div class="invalid-feedback"><span asp-validation-for="Name"></span></div>
</div>