You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: Support Default Error Messages for Observed Forms
This enables developer to remove redundant/repetitive
code from their applications. It also empower developers
to have a central, more ergonomic way to use validation
tools like Zod.
Copy file name to clipboardexpand all lines: TODO.md
+1
Original file line number
Diff line number
Diff line change
@@ -6,6 +6,7 @@
6
6
7
7
## Documentation
8
8
9
+
-[ ] Add more detailed examples of how to use `Zod` with the `defaultErrors.validate` option.
9
10
-[ ] Figure out a Logo for the `enthusiastic-js` Organization and maybe the Form Observer package?
10
11
-[ ] In the interest of time, we're probably going to have to do the bare minimum when it comes to the documentation. Make the API clear, give some helpful examples, etc. After we've release the first draft of the project, we can start thinking about how to "perfect" the docs. But for now, don't get too paranoid about the wording.
11
12
-[ ] Adding demos somewhere in this repo (or in something like a CodeSandbox) would likely be helpful for developers. **Edit**: We now have examples for the `FormValidityObserver`. Would examples for the `FormObserver` or the `FormStorageObserver` also be helpful?
Configures the default error messages to display for the validation constraints. (See the <a href="#method-formvalidityobserverconfigureename-string-errormessages-validationerrorsm-e-void"><code>configure</code></a> method for more details about error message configuration, and refer to the <a href="./types.md#validationerrorsm-e"><code>ValidationErrors</code></a> type for more details about validation constraints.)
123
+
</p>
124
+
<p>
125
+
<blockquote>
126
+
<strong>Note: The <code>defaultErrors.validate</code> option will provide a default custom validation function for <em>all</em> fields in your form.</strong> This is primarily useful if you have a reusable validation function that you want to apply to all of your form's fields (for example, if you are using <a href="https://zod.dev">Zod</a>). See <a href="./guides.md#getting-the-most-out-of-the-defaulterrors-option"><i>Getting the Most out of the <code>defaultErrors</code></i> Option</a> for examples on how to use this option effectively.
127
+
</blockquote>
128
+
</p>
129
+
</dd>
119
130
</dl>
120
131
</dd>
121
132
</dl>
@@ -204,11 +215,11 @@ form1.elements[0].dispatchEvent(new FocusEvent("focusout")); // Does nothing
Configures the error messages that will be displayed for a form field's validation constraints. If an error message is not configured for a validation constraint, then the field's [`validationMessage`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/validationMessage) will be used instead. For [native form fields](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/elements), the browser automatically supplies a default `validationMessage` depending on the broken constraint.
218
+
Configures the error messages that will be displayed for a form field's validation constraints. If an error message is not configured for a validation constraint and there is no corresponding [default configuration](#form-validity-observer-options-default-errors), then the field's [`validationMessage`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/validationMessage) will be used instead. For [native form fields](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/elements), the browser automatically supplies a default `validationMessage` depending on the broken constraint.
208
219
209
-
> Note: If the field is _only_ using the browser's default error messages, it does _not_ need to be `configure`d.
220
+
> Note: If the field is _only_ using the configured [`defaultErrors`](#form-validity-observer-options-default-errors) and/or the browser's default error messages, it _does not_ need to be `configure`d.
210
221
211
-
The Form Element Type, `E`, represents the form field being configured. This type is inferred from the `errorMessages` configuration and defaults to a general [`ValidatableField`](./types.md#validatablefield).
222
+
The Field Element Type, `E`, represents the form field being configured. This type is inferred from the `errorMessages` configuration and defaults to a general [`ValidatableField`](./types.md#validatablefield).
212
223
213
224
#### Parameters
214
225
@@ -297,7 +308,7 @@ When the `focus` option is `false`, you can consider `validateField()` to be an
297
308
298
309
Marks the form field having the specified `name` as invalid (via the [`[aria-invalid="true"]`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-invalid) attribute) and applies the provided error `message` to it. Typically, you shouldn't need to call this method manually; but in rare situations it might be helpful.
299
310
300
-
The Form Element Type, `E`, represents the invalid form field. This type is inferred from the error `message` if it is a function. Otherwise, `E` defaults to a general [`ValidatableField`](./types.md#validatablefield).
311
+
The Field Element Type, `E`, represents the invalid form field. This type is inferred from the error `message` if it is a function. Otherwise, `E` defaults to a general [`ValidatableField`](./types.md#validatablefield).
-[Keeping Track of Visited/Dirty Fields](#keeping-track-of-visiteddirty-fields)
7
+
-[Getting the Most out of the `defaultErrors` option](#getting-the-most-out-of-the-defaulterrors-option)
7
8
-[Keeping Track of Form Data](#keeping-track-of-form-data)
8
9
-[Recommendations for Conditionally Rendered Fields](#recommendations-for-conditionally-rendered-fields)
9
10
-[Recommendations for Styling Form Fields and Their Error Messages](#recommendations-for-styling-form-fields-and-their-error-messages)
@@ -129,6 +130,150 @@ To get an idea of how these event listeners would function, you can play around
129
130
130
131
You can learn more about what can be done with forms using pure JS on our [Philosophy](../extras/philosophy.md#avoid-unnecessary-overhead-and-reinventing-the-wheel) page.
131
132
133
+
## Getting the Most out of the `defaultErrors` Option
134
+
135
+
Typically, we want the error messages in our application to be consistent. Unfortunately, this can sometimes cause us to write the same error messages over and over again. For example, consider a message that might be displayed for the `required` constraint:
observer.configure("first-name", { required:"First Name is required." });
160
+
observer.configure("last-name", { required:"Last Name is required." });
161
+
observer.configure("email", { required:"Email is required." });
162
+
// Configurations for other fields ...
163
+
```
164
+
165
+
But this is redundant (and consequently, error-prone). Since all of our error messages for the `required` constraint follow the same format (`"<FIELD_NAME> is required"`), it would be better for us to use the [`defaultErrors`](./README.md#form-validity-observer-options-default-errors) configuration option instead.
required: (field) =>`${field.labels?.[0].textContent??"This field"} is required.`;
171
+
},
172
+
});
173
+
```
174
+
175
+
This gives us one consistent way to define the `required` error message for _all_ of our fields. Of course, it's possible that not all of your form controls will be labeled by a `<label>` element. For instance, a `radiogroup` is typically labeled by a `<legend>` instead. In this case, you may choose to make the error message more generic
176
+
177
+
```js
178
+
const observer = new FormValidityObserver("focusout", {
179
+
defaultErrors: { required: "This field is required" },
180
+
});
181
+
```
182
+
183
+
Or you may choose to make the error message more flexible
184
+
185
+
```js
186
+
const observer = new FormValidityObserver("focusout", {
187
+
defaultErrors: {
188
+
required(field) {
189
+
if (field instanceof HTMLInputElement && field.type === "radio") {
return `${legend?.textContent ?? "This field"} is required.`;
193
+
}
194
+
195
+
return `${field.labels?.[0].textContent ?? "This field"} is required.`;
196
+
},
197
+
},
198
+
});
199
+
```
200
+
201
+
And if you ever need a _unique_ error message for a specific field, you can still configure it explicitly.
202
+
203
+
```js
204
+
const observer = new FormValidityObserver("focusout", {
205
+
defaultErrors: { required: "This field is required" },
206
+
});
207
+
208
+
observer.configure("my-unique-field", { required: "This field has a unique `required` error!" });
209
+
```
210
+
211
+
### Default Validation Functions
212
+
213
+
The `validate` option in the `defaultErrors` object provides a default custom validation function for _all_ of the fields in your form. This can be helpful if you have a reusable validation function that you want to apply to all of your form's fields. For example, if you're using [`Zod`](https://zod.dev) to validate your form data, you could do something like this:
214
+
215
+
```js
216
+
const schema = z.object({
217
+
"first-name": z.string(),
218
+
"last-name": z.string(),
219
+
email: z.string().email(),
220
+
});
221
+
222
+
const observer = new FormValidityObserver("focusout", {
223
+
defaultErrors: {
224
+
validate(field) {
225
+
const result = schema.shape[field.name].safeParse(field.value);
226
+
// Extract field's error message from `result`
227
+
return errorMessage;
228
+
},
229
+
},
230
+
});
231
+
```
232
+
233
+
By leveraging `defaultErrors.validate`, you can easily use Zod (or any other validation tool) on your frontend. If you're using an SSR framework, you can use the exact same tool on your backend. It's the best of both worlds!
234
+
235
+
### Zod Validation with Nested Fields
236
+
237
+
For more complex form structures (e.g., "Nested Fields" as objects or arrays), you will need to write some advanced logic to make sure that you access the correct `safeParse` function. For example, if you have a field named `address.name.first`, then you'll need to recursively follow the path from `address` to `first` to access the correct `safeParse` function. The [`shape`](https://zod.dev/?id=shape) property (for objects) and the [`element`](https://zod.dev/?id=element) property (for arrays) in Zod will help you accomplish this. Alternatively, you can flatten your object structure entirely:
238
+
239
+
```js
240
+
constschema=z.object({
241
+
"address.name.first":z.string(),
242
+
"address.name.last":z.string(),
243
+
"address.city":z.string(),
244
+
// Other fields...
245
+
});
246
+
```
247
+
248
+
This enables you to use the approach that we showed above without having to write any recursive logic. It's arguably more performant than defining and walking through nested objects, but it requires you to be doubly sure that you're spelling all of your fields' names correctly. Also note that the logic for handling arrays in this example would still take a little effort and may require some recursion. However, this logic shouldn't be too difficult to write.
249
+
250
+
If there's sufficient interest from the community, then we may add some Zod helper functions to our packages to take this burden off of developers.
251
+
252
+
### Zod Validation Using Existing Libraries
253
+
254
+
Another option is to use an existing library that validates forms with Zod (e.g., `@conform-to/zod`) and to extract the error messages from that tool. For example, you might do something like the following:
Copy file name to clipboardexpand all lines: docs/form-validity-observer/integrations/README.md
+22-18
Original file line number
Diff line number
Diff line change
@@ -128,13 +128,14 @@ We'll walk you through the process by going step-by-step on how we made our `Sve
128
128
The first step is easy. Just create a function that instantiates and returns a `FormValidityObserver`. Because this function will only be creating an augmented `FormValidityObserver`, it should accept the same arguments as the class's [constructor](../README.md#constructor-formvalidityobservertypes-options). The return type will be an `interface` that represents the enhanced observer, but we won't add anything to it yet.
129
129
130
130
```ts
131
-
import type { EventType, OneOrMany, FormValidityObserverOptions } from "@form-observer/core";
131
+
import type { EventType, OneOrMany, ValidatableField, FormValidityObserverOptions } from "@form-observer/core";
constobserver=newFormValidityObserver(types, options) as unknown as SvelteFormValidityObserver<M>;
165
167
166
168
/* ---------- Bindings ---------- */
@@ -193,14 +195,15 @@ In this step, we create a reusable utility function that will enable us to autom
193
195
Most JS frameworks create a way for you to accomplish this easily with utility functions. In [`React`](https://react.dev/reference/react-dom/components/common#ref-callback) or [`Vue`](https://vuejs.org/api/built-in-special-attributes.html#ref), you would pass a `ref` callback to an `HTMLFormElement`. In `Svelte`, the idiomatic way to accomplish this is with [`actions`](https://learn.svelte.dev/tutorial/actions):
Copy file name to clipboardexpand all lines: docs/form-validity-observer/integrations/preact.md
+1-1
Original file line number
Diff line number
Diff line change
@@ -81,7 +81,7 @@ Remember that `autoObserve` is simply a convenience utility for calling `observe
81
81
82
82
An enhanced version of [`FormValidityObserver.configure`](../README.md#method-formvalidityobserverconfigureename-string-errormessages-validationerrorsm-e-void) for `Preact`. In addition to configuring a field's error messages, it generates the props that should be applied to the field based on the provided arguments.
83
83
84
-
> Note: If the field is _only_ using the browser's default error messages, it does _not_ need to be `configure`d.
84
+
> Note: If the field is _only_ using the configured [`defaultErrors`](../README.md#form-validity-observer-options-default-errors) and/or the browser's default error messages, it _does not_ need to be `configure`d.
85
85
86
86
The `PreactValidationErrors<M, E>` type is an enhanced version of the core [`ValidationErrors<M, E>`](../types.md#validationerrorsm-e) type. Here is how `PreactValidationErrors` compares to `ValidationErrors`.
Copy file name to clipboardexpand all lines: docs/form-validity-observer/integrations/react.md
+1-1
Original file line number
Diff line number
Diff line change
@@ -91,7 +91,7 @@ Remember that `autoObserve` is simply a convenience utility for calling `observe
91
91
92
92
An enhanced version of [`FormValidityObserver.configure`](../README.md#method-formvalidityobserverconfigureename-string-errormessages-validationerrorsm-e-void) for `React`. In addition to configuring a field's error messages, it generates the props that should be applied to the field based on the provided arguments.
93
93
94
-
> Note: If the field is _only_ using the browser's default error messages, it does _not_ need to be `configure`d.
94
+
> Note: If the field is _only_ using the configured [`defaultErrors`](../README.md#form-validity-observer-options-default-errors) and/or the browser's default error messages, it _does not_ need to be `configure`d.
95
95
96
96
The `ReactValidationErrors<M, E>` type is an enhanced version of the core [`ValidationErrors<M, E>`](../types.md#validationerrorsm-e) type. Here is how `ReactValidationErrors` compares to `ValidationErrors`.
Copy file name to clipboardexpand all lines: docs/form-validity-observer/integrations/solid.md
+1-1
Original file line number
Diff line number
Diff line change
@@ -49,7 +49,7 @@ Remember that `autoObserve` is simply a convenience utility for calling `observe
49
49
50
50
An enhanced version of [`FormValidityObserver.configure`](../README.md#method-formvalidityobserverconfigureename-string-errormessages-validationerrorsm-e-void) for `Solid`. In addition to configuring a field's error messages, it generates the props that should be applied to the field based on the provided arguments.
51
51
52
-
> Note: If the field is _only_ using the browser's default error messages, it does _not_ need to be `configure`d.
52
+
> Note: If the field is _only_ using the configured [`defaultErrors`](../README.md#form-validity-observer-options-default-errors) and/or the browser's default error messages, it _does not_ need to be `configure`d.
53
53
54
54
The `SolidValidationErrors<M, E>` type is an enhanced version of the core [`ValidationErrors<M, E>`](../types.md#validationerrorsm-e) type. Here is how `SolidValidationErrors` compares to `ValidationErrors`.
0 commit comments