ASP.NET Core – Client-side custom validation attributes

I wrote about how to add custom validation attributes. These are used for model validation on the server-side. You can also use these for client-side validation, which I’ll show in this article.

1 – Implement IClientModelValidator

The first step is to implement the IClientModelValidator interface in the custom validation attribute class. This has a single method – AddValidation() – which you use to add data validation HTML attributes (not to be confused with C# attributes):

  • data-val=”true”.
  • data-val-futuredatetime=”<field name> must be a future date”.

Here’s an example of how to implement this method:

using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using System.ComponentModel.DataAnnotations; public class FutureDateTimeAttribute : ValidationAttribute, IClientModelValidator //1. add this interface { public override bool IsValid(object value) { if (value is DateTimeOffset dateTimeOffset && dateTimeOffset > DateTimeOffset.Now) return true; return false; } public override string FormatErrorMessage(string name) { return $"{name} must be a future date"; } //2. implement this method public void AddValidation(ClientModelValidationContext context) { var fieldName = context.Attributes["name"]; context.Attributes.TryAdd("data-val", "true"); context.Attributes.TryAdd("data-val-futuredatetime", FormatErrorMessage(fieldName)); } }
Code language: C# (cs)

Let’s say you’re using FutureDateTimeAttribute on a property called Showtime:

public class MovieTicketOrder { public string Movie { get; set; } [FutureDateTime] public DateTimeOffset? Showtime { get; set; } }
Code language: C# (cs)

When you add this property to a form with TagHelper, it’ll call AddValidation(), resulting in it adding the two HTML attributes:

<input data-val="true" data-val-futuredatetime="Showtime must be a future date" id="Showtime" name="Showtime" type="text" value="">
Code language: HTML, XML (xml)

2 – Add jQuery Validation function and adapter

MVC projects include jQuery Validation and jQuery Validation Unobtrusive for client-side validation by default. So the next step is to implement your validation logic in a jQuery Validation function and adapter, like this:

/* jQuery Validation function and adapter in /wwwroot/js/customValidator.js */ $.validator.addMethod('futuredatetime', function (value, element, params) { return new Date(value) > new Date(); //it's valid if it's a future date }); $.validator.unobtrusive.adapters.add('futuredatetime', function (options) { options.rules['futuredatetime'] = []; options.messages['futuredatetime'] = options.message; });
Code language: JavaScript (javascript)

When the user inputs a value, it validates the field with data-val-futuredatetime by calling the futuredatetime function. They’re linked together by name.

3 – Include the validation scripts

In /Views/Shared/_ValidationScriptsPartial.cshtml, add your custom validation script:

<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script> <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script> <script src="~/js/customValidator.js"></script>
Code language: HTML, XML (xml)

Finally, add _ValidationScriptsPartial to your View. Here is the View with _ValidationScriptsPartial loaded at the end:

@model MoviesMVC.Models.MovieTicketOrder @using (Html.BeginForm()) { <div> <div> @Html.LabelFor(m => m.Movie) @Html.TextBoxFor(m => m.Movie) @Html.ValidationMessageFor(m => m.Movie) </div> <div> @Html.LabelFor(m => m.Showtime) @Html.TextBoxFor(m => m.Showtime) @Html.ValidationMessageFor(m => m.Showtime) </div> <div> <input type="submit" value="Add movie" /> </div> </div> } @section Scripts { @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } }
Code language: HTML, XML (xml)

Here’s what this looks like in the browser when you enter an invalid date:

Showing client-side validation error on the form: "Showtime must be a future date"

Leave a Comment