Client-Side Validation — Unobtrusive jQuery Validation

Client-side validation runs in the browser using jQuery Validation and jQuery Unobtrusive Validation. It reads the data-val-* attributes generated by Tag Helpers and DataAnnotations, validates field values before the form is submitted, and shows error messages inline — without a round-trip to the server. This improves perceived responsiveness: validation errors appear instantly as the user fills the form. Importantly, client-side validation is a convenience feature, not a security measure — it can always be bypassed. Server-side validation is the authoritative check; client-side is a UX enhancement.

Setting Up Client-Side Validation

@* ── Include validation scripts (in the Scripts section of the view) ─────── *@
@section Scripts {
    @* _ValidationScriptsPartial includes jquery.validate.min.js
       and jquery.validate.unobtrusive.min.js *@
    <partial name="_ValidationScriptsPartial" />
}

@* ── What the Tag Helper generates for a validated field ────────────────── *@
@* Input in the view: *@
<input asp-for="Title" class="form-control" />

@* Generated HTML — data-val-* attributes drive client-side validation: *@
<input type="text"
       id="Title"
       name="Title"
       value=""
       class="form-control"
       data-val="true"
       data-val-required="Title is required."
       data-val-length="Title must be between 5 and 200 characters."
       data-val-length-min="5"
       data-val-length-max="200" />

@* ── The corresponding error span ────────────────────────────────────────── *@
<span asp-validation-for="Title"
      class="text-danger field-validation-valid">
      @* filled by jquery.validate.unobtrusive when validation fails *@
</span>
Note: The _ValidationScriptsPartial.cshtml bundled with the MVC template references jQuery Validation and jQuery Unobtrusive Validation from the wwwroot/lib/ folder. If these libraries are not present (e.g., after running npm install or LibMan to manage client-side libraries), client-side validation silently stops working — forms submit without client-side checks and only server-side validation catches errors. Run libman restore or check wwwroot/lib/jquery-validate/ exists after cloning the repository.
Tip: For forms loaded dynamically via AJAX, call $.validator.unobtrusive.parse(formElement) after injecting the form into the DOM. jQuery Unobtrusive Validation only attaches itself to forms present at DOMContentLoaded — forms injected later (modal dialogs, tab panels loaded via AJAX) need to be explicitly parsed. Forgetting this is the most common cause of “client-side validation not working in a modal form.”
Warning: Client-side validation is a browser-side JavaScript feature. It can be bypassed by: disabling JavaScript, using browser developer tools to remove data-val attributes, using Postman or curl to submit directly to the endpoint, or using a malicious browser extension. Always treat client-side validation as a user experience enhancement only. Every form submission must have server-side validation as the authoritative check. Never rely on client-side validation for security decisions.

Custom Client-Side Validator

// ── To add client-side support for a custom ValidationAttribute, ──────────
// implement IClientModelValidator on the attribute:

public sealed class SlugFormatAttribute : ValidationAttribute, IClientModelValidator
{
    // ... server-side IsValid implementation (from previous lesson) ...

    // Client-side: adds data-val-* attributes that jQuery Validate reads
    public void AddValidation(ClientModelValidationContext context)
    {
        context.Attributes["data-val"] = "true";
        context.Attributes["data-val-slugformat"] = FormatErrorMessage(context.ModelMetadata.DisplayName);
    }
}

// ── Register the custom jQuery validator (in site.js or a dedicated script) ─
@* In the Scripts section of any view using SlugFormat: *@
<script>
// Register the custom validator method
$.validator.addMethod("slugformat", function (value, element) {
    return this.optional(element) || /^[a-z0-9-]+$/.test(value);
});

// Register the unobtrusive adapter to connect data-val-slugformat to the method
$.validator.unobtrusive.adapters.addBool("slugformat");
</script>

Common Mistakes

Mistake 1 — Not including _ValidationScriptsPartial (client validation silently disabled)

❌ Wrong — validation scripts not loaded; forms submit without client checks:

@section Scripts { }   @* empty — no validation scripts loaded *@

✅ Correct — always include <partial name="_ValidationScriptsPartial" /> in the Scripts section of forms.

Mistake 2 — Trusting client-side validation for security decisions

❌ Wrong — skipping server-side validation because “the client already validated it.”

✅ Correct — always check ModelState.IsValid server-side regardless of client-side state.

🧠 Test Yourself

Client-side validation blocks a form submission because a field is invalid. A developer removes the data-val-required attribute using browser developer tools and submits. What happens?