Wiki source code of Async Form Validation
Version 1.1 by Marius Dumitru Florea on 2022/09/13
Hide last authors
author | version | line-number | content |
---|---|---|---|
![]() |
1.1 | 1 | When working with HTML forms a recurring need is to be able to validate the form data. This is normally done both: |
2 | |||
3 | * on the client-side (when the user inputs the data or before the data is submitted) and | ||
4 | * on the server-side (after the data is submitted). | ||
5 | |||
6 | On the client-side, the validation can be done both: | ||
7 | |||
8 | * synchronously (e.g. when checking if the mandatory fields are filled and if the field values match the expected format) | ||
9 | * and asynchronously, when the JavaScript code doesn't have all the information needed for the validation so it has to make one or more HTTP requests to the server-side (e.g. to check if the typed page name exists already) | ||
10 | |||
11 | When a form is validated asynchronously we usually want to: | ||
12 | |||
13 | * postpone the submit until all validations are executed (until there are no pending validations) | ||
14 | ** allow the user to trigger the form submit even if there are pending validations, but disable the form while waiting for them to be executed | ||
15 | * prevent the submit if there are failed validations (e.g. by disabling the submit button) | ||
16 | * be able to abort / replace a previous validation for a specific field when the user changes its value | ||
17 | ** re-enable the submit button when a failed validation is replaced, if there are no other failed validations | ||
18 | * be able to delay the validation in order to give the user the chance to type (we don't want to validate after each keystroke because HTTP requests are expensive) | ||
19 | |||
20 | This can be achieved using the ##xwiki-form-validation-async## JavaScript module that provides a jQuery plugin. Suppose you have the following HTML form: | ||
21 | |||
22 | {{code language="html"}} | ||
23 | <form> | ||
24 | <!-- The fieldset is needed in order to be able to disable the entire form easily. --> | ||
25 | <fieldset> | ||
26 | <!-- This is the field that needs to be validated asynchronously. --> | ||
27 | <input type="text" name="title" value="" /> | ||
28 | <span class="xErrorMsg hidden"></span> | ||
29 | <input type="submit" value="Submit" /> | ||
30 | </fieldset> | ||
31 | </form> | ||
32 | {{/code}} | ||
33 | |||
34 | You can implement asynchronous validation like this: | ||
35 | |||
36 | {{code language="js"}} | ||
37 | require(['jquery', 'xwiki-form-validation-async'], function($) { | ||
38 | const titleInput = $('input[name=title]'); | ||
39 | const titleError = titleInput.next('.xErrorMsg'); | ||
40 | |||
41 | const validateTitle = () => { | ||
42 | if (!titleInput.val()) { | ||
43 | return Promise.reject('Please enter the title.'); | ||
44 | } else { | ||
45 | return new Promise((resolve, reject) => { | ||
46 | // Perform the asynchronous validation, calling resolve() or reject('error message') when done. | ||
47 | ... | ||
48 | }); | ||
49 | } | ||
50 | }; | ||
51 | |||
52 | titleInput.on('input', () => { | ||
53 | // Hide the last error message whenever the user changes the value. | ||
54 | titleError.addClass('hidden'); | ||
55 | // Show a visual indicator while the validation is in progress. | ||
56 | titleInput.addClass('loading'); | ||
57 | // Schedule the asynchronous validation after 500ms. The namespace is used to prevent replacing validations added by | ||
58 | // other modules. | ||
59 | titleInput.validateAsync(validateTitle, /* delay: */ 500, /* namespace: */ 'myModule').catch(error => { | ||
60 | // Show the error message. Note that this code is executed only if the validation has not become outdated since it | ||
61 | // was scheduled. | ||
62 | titleError.removeClass('hidden').text(error); | ||
63 | }).finally(() => { | ||
64 | // Remove the visual indicator once the validation is done. Note that this code is executed only if the validation | ||
65 | // has not become outdated since it was scheduled. | ||
66 | titleInput.removeClass('loading'); | ||
67 | }); | ||
68 | }); | ||
69 | }); | ||
70 | {{/code}} |