diff --git a/Pages/Demos/DelayedValidation.cshtml b/Pages/Demos/DelayedValidation.cshtml
new file mode 100644
index 0000000..358ea2c
--- /dev/null
+++ b/Pages/Demos/DelayedValidation.cshtml
@@ -0,0 +1,28 @@
+@page
+@model DemoWeb.Pages.Demos.DelayedValidation
+@{
+ Layout = "Shared/_Layout";
+}
+
+
+ Please correct the following errors
+
+
+
+
+@section Scripts {
+
+}
\ No newline at end of file
diff --git a/Pages/Demos/DelayedValidation.cshtml.cs b/Pages/Demos/DelayedValidation.cshtml.cs
new file mode 100644
index 0000000..6e5aac2
--- /dev/null
+++ b/Pages/Demos/DelayedValidation.cshtml.cs
@@ -0,0 +1,14 @@
+using System.ComponentModel.DataAnnotations;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.Mvc.Rendering;
+
+namespace DemoWeb.Pages.Demos;
+
+public class DelayedValidation : PageModel
+{
+ [BindProperty]
+ [Required]
+ [EmailAddress]
+ public string? EmailAddress { get; set; }
+}
\ No newline at end of file
diff --git a/Pages/Index.cshtml b/Pages/Index.cshtml
index c2cf5b7..746587f 100644
--- a/Pages/Index.cshtml
+++ b/Pages/Index.cshtml
@@ -14,6 +14,7 @@
Form Action
Disabled inputs ({watch: true}
)
Disabled inputs (no watch)
+ Delayed Validation
@if (Model.StatusMessage != null) {
diff --git a/dist/aspnet-validation.js b/dist/aspnet-validation.js
index 94b58d5..8372f15 100644
--- a/dist/aspnet-validation.js
+++ b/dist/aspnet-validation.js
@@ -690,6 +690,7 @@ var ValidationService = /** @class */ (function () {
root: document.body,
watch: false,
addNoValidate: true,
+ delayedValidation: false,
};
/**
* Override CSS class name for input validation error. Default: 'input-validation-error'
@@ -1096,6 +1097,12 @@ var ValidationService = /** @class */ (function () {
validate = this.validators[uid];
if (!validate)
return [2 /*return*/, true];
+ if (this.options.delayedValidation &&
+ event && event.type === 'input' &&
+ !input.classList.contains(this.ValidationInputCssClassName)) {
+ // When delayedValidation=true, "input" only takes it back to valid. "Change" can make it invalid.
+ return [2 /*return*/, true];
+ }
this.logger.log('Validating', { event: event });
_a.label = 1;
case 1:
@@ -1120,9 +1127,17 @@ var ValidationService = /** @class */ (function () {
cb(event, callback);
}, _this.debounce);
};
- var validateEvent = input.dataset.valEvent || input instanceof HTMLSelectElement ? 'change' : 'input';
- input.addEventListener(validateEvent, cb.debounced);
- cb.remove = function () { return input.removeEventListener(validateEvent, cb.debounced); };
+ var validateEvent = input.dataset.valEvent || input instanceof HTMLSelectElement ? 'change' :
+ (this.options.delayedValidation ? 'input change' : 'input');
+ var events = validateEvent.split(' ');
+ events.forEach(function (eventName) {
+ input.addEventListener(eventName, cb.debounced);
+ });
+ cb.remove = function () {
+ events.forEach(function (eventName) {
+ input.removeEventListener(eventName, cb.debounced);
+ });
+ };
this.inputEvents[uid] = cb;
};
ValidationService.prototype.removeInput = function (input) {
@@ -1378,6 +1393,7 @@ var ValidationService = /** @class */ (function () {
* Load default validation providers and scans the entire document when ready.
* @param options.watch If set to true, a MutationObserver will be used to continuously watch for new elements that provide validation directives.
* @param options.addNoValidate If set to true (the default), a novalidate attribute will be added to the containing form in validate elements.
+ * @param options.delayedValidation If set to false (the default), validation happens while user inputs. If set to true, validation happens on blur, unless input is already invalid, in which case it will validate on input to indicate the value is valid as soon as possible.
*/
ValidationService.prototype.bootstrap = function (options) {
var _this = this;
diff --git a/dist/aspnet-validation.min.js b/dist/aspnet-validation.min.js
index 231cb0e..5f7108c 100644
--- a/dist/aspnet-validation.min.js
+++ b/dist/aspnet-validation.min.js
@@ -1,2 +1,2 @@
-!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.aspnetValidation=t():e.aspnetValidation=t()}(window,(function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t),n.d(t,"isValidatable",(function(){return s})),n.d(t,"MvcValidationProviders",(function(){return d})),n.d(t,"ValidationService",(function(){return c}));var r=function(e,t,n,r){return new(n||(n=Promise))((function(i,a){function s(e){try{l(r.next(e))}catch(e){a(e)}}function o(e){try{l(r.throw(e))}catch(e){a(e)}}function l(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,o)}l((r=r.apply(e,t||[])).next())}))},i=function(e,t){var n,r,i,a,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return a={next:o(0),throw:o(1),return:o(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function o(o){return function(l){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;a&&(a=0,o[0]&&(s=0)),s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]-1){var a=n.substring(0,i)+"."+r,o=document.getElementsByName(a)[0];if(s(o))return o}return e.form.querySelector(l("[name=".concat(r,"]")))}var d=function(){this.required=function(e,t,n){var r=t.type.toLowerCase();if("checkbox"===r||"radio"===r){for(var i=0,a=Array.from(t.form.querySelectorAll(l("[name='".concat(t.name,"'][type='").concat(r,"']"))));ii)return!1}return!0},this.compare=function(e,t,n){if(!n.other)return!0;var r=u(t,n.other);return!r||r.value===e},this.range=function(e,t,n){if(!e)return!0;var r=parseFloat(e);return!isNaN(r)&&(!(n.min&&rparseFloat(n.max)))},this.regex=function(e,t,n){return!e||!n.pattern||new RegExp(n.pattern).test(e)},this.email=function(e,t,n){return!e||/^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*(\.\w{2,})+$/.test(e)},this.creditcard=function(e,t,n){if(!e)return!0;if(/[^0-9 \-]+/.test(e))return!1;var r,i,a=0,s=0,o=!1;if((e=e.replace(/\D/g,"")).length<13||e.length>19)return!1;for(r=e.length-1;r>=0;r--)i=e.charAt(r),s=parseInt(i,10),o&&(s*=2)>9&&(s-=9),a+=s,o=!o;return a%10==0},this.url=function(e,t,n){if(!e)return!0;var r=e.toLowerCase();return r.indexOf("http://")>-1||r.indexOf("https://")>-1||r.indexOf("ftp://")>-1},this.phone=function(e,t,n){return!e||!/[\+\-\s][\-\s]/g.test(e)&&/^\+?[0-9\-\s]+$/.test(e)},this.remote=function(e,t,n){if(!e)return!0;for(var r=n.additionalfields.split(","),i={},a=0,s=r;a=200&&r.status<300){var i=JSON.parse(r.responseText);e(i)}else t({status:r.status,statusText:r.statusText,data:r.responseText})},r.onerror=function(e){t({status:r.status,statusText:r.statusText,data:r.responseText})}}))}},c=function(){function e(e){var t=this;this.providers={},this.messageFor={},this.elementUIDs=[],this.elementByUID={},this.formInputs={},this.validators={},this.formEvents={},this.inputEvents={},this.summary={},this.debounce=300,this.allowHiddenFields=!1,this.validateForm=function(e,n){return r(t,void 0,void 0,(function(){var t,r,a;return i(this,(function(i){switch(i.label){case 0:if(!(e instanceof HTMLFormElement))throw new Error("validateForm() can only be called on