ASP.NET Unobtrusive Validation. Группируем Сообщения об Ошибках
Сегодня пост из личного опыта. Немного легаси и немного «попрограммируем» на HTML+CSS 😊
Не знаю, как дела обстоят в UI фреймворках, таких, как React или Vue, т.к. не знаком с ними, но мы в компании до сих пор используем старый добрый HTML и jQuery. В том числе для валидации форм используется встроенная в ASP.NET jQuery Unobtrusive Validation. Она позволяет проверять формы на клиенте без написания логики валидации на JS. То есть в модели используются стандартные атрибуты Data Annotations.
Например, для модели:
public class MyModel
{
[Required]
public string Name { get; set; }
}
И формы
<form method="post">
Email: <input asp-for="Name" /> <br />
<span asp-validation-for="Name"></span>
…
</form>
Будет сгенерирован следующий HTML код:
<form method="post">
Name: <input name="Name" id="Name" type="text" value=""
data-val-required="The Name field is required."
data-val="true"><br>
<span class="field-validation-valid" data-valmsg-replace="true"
data-valmsg-for="Name"></span>
…
</form>
И эта форма автоматически будет проверяться средствами JS согласно этим атрибутам на клиенте без отправки формы на сервер. В случае непрошедшей валидации в элемент
<span> будет добавляться соответствующее сообщение об ошибке, а его класс сменится с field-validation-valid на field-validation-error. Можно сделать так, что все ошибки формы будут выдаваться в одном месте (validation summary), либо для каждого поля в отдельности (как в этом случае).Конечно, если требуется нестандартная валидация, можно как написать свой атрибут валидации, так и добавить в JS валидатор для него.
Мне же требовалось, чтобы только 2 поля формы поля проверялись в паре и выдавали одно сообщение об ошибке на двоих в одном и том же месте, даже если оба поля не пройдут валидацию. Проблема подробно описана на StackOverflow. Там у автора была дата рождения, у меня аналогично двумя выпадающими списками выбирались месяц и год истечения срока кредитной карты.
Предложенное там решение в виде написания собственного атрибута (плюс валидатора в JS) показалось мне чересчур сложным для такого, вроде бы, тривиального случая. Поэтому решено было действовать методами фронтэнда.
В общем то, решение довольно простое. Размещаем оба поля и оба валидатора для них:
<form method="post">
Expires:
<select asp-for="Month" asp-items="Model.Months"></select> /
<select asp-for="Year" asp-items="Model.Years"></select>
<div class="val-msg-group">
<span asp-validation-for="Month"></span>
<span asp-validation-for="Year"></span>
</div>
…
</form>
В этом случае, если оба поля будут пустыми, будут выведены оба сообщения об ошибке, по одному для каждого. Это решается просто сокрытием второго сообщения средствами CSS:
.val-msg-group .field-validation-error ~ .field-validation-error {
display: none;
}Здесь мы внутри блока
val-msg-group скрываем всех потомков, за которыми следует элемент с тем же классом field-validation-error. То есть, первое сообщение об ошибке будет показано (для какого бы поля оно ни было), а остальные будут спрятаны. См. видео ниже.В общем, думаю, что приём будет полезен и в других UI фреймворках, работающих по подобному принципу.