Skip to content

Commit 9d598f8

Browse files
implement reportValidity for reporting proper form validation errors behind config flag (#3372)
* implement reportvalidity behind feature flag * add tests
1 parent dc80493 commit 9d598f8

File tree

6 files changed

+89
-4
lines changed

6 files changed

+89
-4
lines changed

src/htmx.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,14 @@ var htmx = (function() {
278278
* @type boolean
279279
* @default true
280280
*/
281-
historyRestoreAsHxRequest: true
281+
historyRestoreAsHxRequest: true,
282+
/**
283+
* Weather to report input validation errors to the end user and update focus to the first input that fails validation.
284+
* This should always be enabled as this matches default browser form submit behaviour
285+
* @type boolean
286+
* @default false
287+
*/
288+
reportValidityOfForms: false
282289
},
283290
/** @type {typeof parseInterval} */
284291
parseInterval: null,
@@ -3548,8 +3555,17 @@ var htmx = (function() {
35483555
if (element.willValidate) {
35493556
triggerEvent(element, 'htmx:validation:validate')
35503557
if (!element.checkValidity()) {
3558+
if (
3559+
triggerEvent(element, 'htmx:validation:failed', {
3560+
message: element.validationMessage,
3561+
validity: element.validity
3562+
}) &&
3563+
!errors.length &&
3564+
htmx.config.reportValidityOfForms
3565+
) {
3566+
element.reportValidity()
3567+
}
35513568
errors.push({ elt: element, message: element.validationMessage, validity: element.validity })
3552-
triggerEvent(element, 'htmx:validation:failed', { message: element.validationMessage, validity: element.validity })
35533569
}
35543570
}
35553571
}

test/core/validation.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,72 @@ describe('Core htmx client side validation tests', function() {
176176
calledEvent.should.equal(true)
177177
})
178178

179+
it('foucuses on first invalid input and reports error to user when reportValidityOfForms true', function() {
180+
htmx.config.reportValidityOfForms = true
181+
var form = make('<form hx-post="/test" hx-trigger="click">' +
182+
'No Request' +
183+
'<input id="i1" name="i1" required>' +
184+
'<input id="i2" name="i2" required>' +
185+
'</form>')
186+
var calledEvent = false
187+
var handler = htmx.on(form, 'htmx:validation:failed', function() {
188+
calledEvent = true
189+
})
190+
try {
191+
form.click()
192+
this.server.respond()
193+
} finally {
194+
htmx.off(form, handler)
195+
}
196+
calledEvent.should.equal(true)
197+
document.activeElement.should.equal(byId('i1'))
198+
htmx.config.reportValidityOfForms = false
199+
})
200+
201+
it('no focus on first invalid input or report errors when reportValidityOfForms false', function() {
202+
htmx.config.reportValidityOfForms = false
203+
var form = make('<form hx-post="/test" hx-trigger="click">' +
204+
'No Request' +
205+
'<input id="i1" name="i1" required>' +
206+
'<input id="i2" name="i2" required>' +
207+
'</form>')
208+
var calledEvent = false
209+
var handler = htmx.on(form, 'htmx:validation:failed', function() {
210+
calledEvent = true
211+
})
212+
try {
213+
form.click()
214+
this.server.respond()
215+
} finally {
216+
htmx.off(form, handler)
217+
}
218+
calledEvent.should.equal(true)
219+
document.activeElement.should.equal(document.body)
220+
})
221+
222+
it('can prevent reportValidity with preventDefault when reportValidityOfForms true', function() {
223+
htmx.config.reportValidityOfForms = true
224+
var form = make('<form hx-post="/test" hx-trigger="click">' +
225+
'No Request' +
226+
'<input id="i1" name="i1" required>' +
227+
'<input id="i2" name="i2" required>' +
228+
'</form>')
229+
var calledEvent = false
230+
var handler = htmx.on(form, 'htmx:validation:failed', function(evt) {
231+
calledEvent = true
232+
evt.preventDefault()
233+
})
234+
try {
235+
form.click()
236+
this.server.respond()
237+
} finally {
238+
htmx.off(form, handler)
239+
}
240+
calledEvent.should.equal(true)
241+
document.activeElement.should.equal(document.body)
242+
htmx.config.reportValidityOfForms = false
243+
})
244+
179245
it('calls htmx:validation:halted on failure', function() {
180246
var form = make('<form hx-post="/test" hx-trigger="click">' +
181247
'No Request' +

www/content/api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ Note that using a [meta tag](@/docs.md#config) is the preferred mechanism for se
143143
* `htmx.config.responseHandling:[...]` - HtmxResponseHandlingConfig[]: the default [Response Handling](@/docs.md#response-handling) behavior for response status codes can be configured here to either swap or error
144144
* `htmx.config.allowNestedOobSwaps:true` - boolean: whether to process OOB swaps on elements that are nested within the main response element. See [Nested OOB Swaps](@/attributes/hx-swap-oob.md#nested-oob-swaps).
145145
* `htmx.config.historyRestoreAsHxRequest:true` - Whether to treat history cache miss full page reload requests as a "HX-Request" by returning this response header. This should always be disabled when using HX-Request header to optionally return partial responses
146-
146+
* `htmx.config.reportValidityOfForms:false` - Weather to report input validation errors to the end user and update focus to the first input that fails validation. This should always be enabled as this matches default browser form submit behaviour
147147
##### Example
148148

149149
```js

www/content/docs.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,8 @@ Htmx fires events around validation that can be used to hook in custom validatio
10901090
Non-form elements do not validate before they make requests by default, but you can enable validation by setting
10911091
the [`hx-validate`](@/attributes/hx-validate.md) attribute to "true".
10921092

1093+
Normal browser form submission alerts the user of any validation errors automatically and auto focuses on the first invalid input. For backwards compatibility reasons htmx does not report the validation to the users by default and you should always enable this option by setting `htmx.config.reportValidityOfForms` to `true` to restore the default browser behavior.
1094+
10931095
### Validation Example
10941096

10951097
Here is an example of an input that uses the [`hx-on`](/attributes/hx-on) attribute to catch the

www/content/events.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ to implement custom validation rules.
565565

566566
### Event - `htmx:validation:failed` {#htmx:validation:failed}
567567

568-
This event is triggered when an element fails validation.
568+
This event is triggered when an element fails validation. If `preventDefault()` is invoked on the event, the reportValidity() enabled by `htmx.config.reportValidityOfForms` will not be called.
569569

570570
##### Details
571571

www/content/reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ listed below:
257257
| `htmx.config.responseHandling` | the default [Response Handling](@/docs.md#response-handling) behavior for response status codes can be configured here to either swap or error |
258258
| `htmx.config.allowNestedOobSwaps` | defaults to `true`, whether to process OOB swaps on elements that are nested within the main response element. See [Nested OOB Swaps](@/attributes/hx-swap-oob.md#nested-oob-swaps). |
259259
| `htmx.config.historyRestoreAsHxRequest`| defaults to `true`, Whether to treat history cache miss full page reload requests as a "HX-Request" by returning this response header. This should always be disabled when using HX-Request header to optionally return partial responses |
260+
| `htmx.config.reportValidityOfForms` | defaults to `false`, Weather to report input validation errors to the end user and update focus to the first input that fails validation. This should always be enabled as this matches default browser form submit behaviour |
260261

261262

262263
</div>

0 commit comments

Comments
 (0)