From: Carl Worth Date: Sun, 7 Jun 2020 22:47:36 +0000 (-0700) Subject: Take control over the wording of the validation message for category X-Git-Url: https://git.cworth.org/git?p=lmno.games;a=commitdiff_plain;h=b0b2d2d5dd5c3bac29537e2f5b206208f0a43898;ds=sidebyside Take control over the wording of the validation message for category The "pattern" attribute on the text field was really handy for triggering HTML5 validation that the input matched a regular expression. The only real downside is that it gives a truly generic error message: Please match the requested format. That doesn't actually tell the user waht to fix, (in this case, to add a number). I really wish there existed another attribute to simply provide the error message that should be provided. If there is, I couldn't find one. I did have the title set here, which is close, but with two problems: 1. The resulting error message isn't _just_ my text, it is: Please match the requested format: so I don't quite get as much control as I want. 2. The title text shows up immediately when hovering over the input field, (even before the user has ever typed anything). But this is an error message that I only want the user to see if they've committed an error. It took me quite a lot of struggling to come up with a solution for this minor issue that works the way I want. Here it is: 1. I remove the pattern attribute, (so I don't get any default validation of the input format). This is essential or else the "Please match the requested format" text will appear at least somewhere. 2. In the subit handler, I validate the input against my regular expression and call setCustomValidity on the input field to set the text I want to appear. 3. But _also_ immediately after doing this I _also_ call reportVisibility on the form element. Otherwise, the input field will still get styled as invalid as I want, but the error message won't actually get reported, (unless the user tries submitting _again_ while the input is still invalid). 4. Next I also have to arrange to _clear_ this invalid state. So for this I add a new onChange handler (so it will be called for every keystroke). For this, a lot of tutorials just call setCustomValidity with an empty string here unconditionally. The downside of that is that the field will get styled as valid as soon as the user makes any change. Instead, I check the regular expression here so that the field is only styled as valid once the mistake is corrected. (This is consistent with the behavior of the default HTML5 validation with the "pattern" attribute.) So in the end, this gives the behavior that I want. It's a little wordy, especially here in my explanation!, but also in the code: Particularly that two different handlers are required: One to set the error state and one to clear it. As I implemented things here the regular expression is even duplicated in those two cases, (but that's a defect that could be addressed---the pattern could be stashed in a common place if I cared to do it). Note that it would be possible to set the error state in an 'else' clause within my onChange handler, (and that would eliminate the duplication of the regular expression pattern). The reason I don't do that here is that it would cause the field to be styled with the angry-red "invalid" styling as soon as the user started typing, rather than waiting for form submission before validation happens. So that could be annoying to users (it would drive me crazy) and it would also be inconsistent with how HTML5 validation happens with the "pattern" attribute. --- diff --git a/empathy/empathy.jsx b/empathy/empathy.jsx index 84a35a5..1ee8000 100644 --- a/empathy/empathy.jsx +++ b/empathy/empathy.jsx @@ -127,9 +127,18 @@ class CategoryRequest extends React.Component { super(props); this.category = React.createRef(); + this.handle_change = this.handle_change.bind(this); this.handle_submit = this.handle_submit.bind(this); } + handle_change(event) { + const category_input = this.category.current; + const category = category_input.value; + + if (/[0-9]/.test(category)) + category_input.setCustomValidity(""); + } + handle_submit(event) { const category_input = this.category.current; const category = category_input.value; @@ -137,6 +146,12 @@ class CategoryRequest extends React.Component { /* Prevent the default page-changing form-submission behavior. */ event.preventDefault(); + if (! /[0-9]/.test(category)) { + category_input.setCustomValidity("Category must include a number"); + event.currentTarget.reportValidity(); + return; + } + console.log("Do something here with category: " + category); } @@ -155,8 +170,8 @@ class CategoryRequest extends React.Component { type="text" id="category" placeholder="6 things at the beach" - required pattern=".*[0-9]+.*" - title="Category must contain a number" + required + onChange={this.handle_change} ref={this.category} />