Skip to content

Latest commit

 

History

History
233 lines (174 loc) · 11.8 KB

File metadata and controls

233 lines (174 loc) · 11.8 KB

Solid + Form Validity Observer

A convenience API for reducing code repetition in a Solid application using the FormValidityObserver.

Function: createFormValidityObserver(type, options)

Creates an enhanced version of the FormValidityObserver, known as the SolidFormValidityObserver. It accepts the exact same arguments as the FormValidityObserver's constructor.

Return Type: SolidFormValidityObserver<M, R>

An enhanced version of the FormValidityObserver, designed specifically for Solid applications. It has the same Type Parameters as the FormValidityObserver. As with the FormValidityObserver, the type of M is derived from the renderer option, and the type of R is derived from the renderByDefault option.

Copied Methods

The following methods on the SolidFormValidityObserver are the exact same as the methods on the FormValidityObserver. These methods are bound to the observer instance to allow for safe object destructuring:

Function: autoObserve(form: HTMLFormElement, novalidate: () => boolean): void

A Solid directive used to simplify the process of setting up and cleaning up a form's FormValidityObserver. It does this by calling observe and unobserve automatically with the form on which it is used.

The novalidate option indicates that the novalidate attribute should be applied to the form element when JavaScript is available to the client. By default, its value is true. (For details on why this attribute is significant, see Enabling Accessible Error Messages during Form Submissions.)

Note: If you use this directive, you should not need to call observe, unobserve, or disconnect directly.

Example

import { createFormValidityObserver } from "@form-observer/solid";

function MyForm() {
  const { autoObserve } = createFormValidityObserver("focusout");

  // Or <form use:autoObserve={true}>{/* ... */}</form>
  return <form use:autoObserve>{/* Other Elements */}</form>;
}

Remember that autoObserve is simply a convenience utility for calling observe and unobserve automatically. You're free to setup and teardown the FormValidityObserver manually if you prefer.

Function: configure<E>(name: string, errorMessages: SolidValidationErrors<M, E, R>): SolidFieldProps

An enhanced version of FormValidityObserver.configure 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.

Note: If the field is only using the configured defaultErrors and/or the browser's default error messages, it does not need to be configured.

The SolidValidationErrors<M, E, R> type is an enhanced version of the core ValidationErrors<M, E, R> type. Here is how SolidValidationErrors compares to ValidationErrors.

Properties That Mimic the ValidationErrors Properties

The following properties on the SolidValidationErrors type accept the exact same values as the corresponding properties on ValidationErrors type.

  • badinput
  • validate

Example

import { createFormValidityObserver } from "@form-observer/solid";

function MyForm() {
  const { autoObserve, configure } = createFormValidityObserver("focusout");

  function validateConfirmPassword(field: HTMLInputElement): string | void {
    const password = field.form?.elements.namedItem("password") as HTMLInputElement;
    return field.value === password.value ? undefined : "Passwords do not match.";
  }

  async function validateNewUsername(field: HTMLInputElement): Promise<string | void> {
    const response = await fetch("/api/username-exists", { body: field.value });
    const usernameTaken = await response.text();
    return usernameTaken === String(true) ? "Username is already taken" : undefined;
  }

  return (
    <form use:autoObserve>
      {/* Note: Accessible <label>s and error containers were omitted from this example. */}
      <input {...configure("username", { validate: validateNewUsername })} />
      <input name="password" type="password" />
      <input {...configure("confirm-password", { validate: validateConfirmPassword })} />

      <input type="date" {...configure("date", { badinput: "Please provide a valid date." })} />
    </form>
  );
}
Properties That Enhance the ValidationErrors Properties

All the other properties on the SolidValidationErrors type are enhancements of the corresponding properties on the ValidationErrors type, so they follow slightly different rules. For clarity, these "other properties" are:

  • required
  • minlength
  • min
  • maxlength
  • max
  • step
  • type
  • pattern

The rules are as follows:

1) When a constraint is configured with an ErrorDetails object, the object must include a value property specifying the value of the constraint. In this scenario, both the field's constraint value and its error message are configured.

import { createFormValidityObserver } from "@form-observer/solid";
import type { ValidatableField } from "@form-observer/solid";

function MyForm() {
  const { autoObserve, configure } = createFormValidityObserver("focusout");
  const requiredField = (field: ValidatableField) => `<p>${field.labels[0]?.textContent ?? "Field"} is required.</p>`;

  return (
    <form use:autoObserve>
      {/* Note: Accessible <label>s and error containers were omitted from this example. */}
      <input {...configure("name", { required: { value: true, message: requiredField, render: true } })} />
      <input {...configure("email", { type: { value: "email", message: "Email is invalid", render: false } })} />
      <input
        {...configure("comment", { maxlength: { value: 80, message: "Comment must be 80 characters or less" } })}
      />
    </form>
  );
}

Note: A constraint can only be configured with an error message when you use the object syntax. The exception to this rule is the required constraint, which allows you to imply a value of true when you supply an error message value directly to the constraint.

import { createFormValidityObserver } from "@form-observer/solid";
import type { ValidatableField } from "@form-observer/solid";

function MyForm() {
  const { autoObserve, configure } = createFormValidityObserver("focusout");
  const requiredField = (field: ValidatableField) => `${field.labels[0]?.textContent ?? "Field"} is required.`;

  return (
    <form use:autoObserve>
      {/* Note: Accessible <label>s and error containers were omitted from this example. */}
      <input {...configure("first-name", { required: requiredField })} />
      <input {...configure("last-name", { required: "Don't ignore me..." })} />
      <input {...configure("email", { required: { value: true, message: requiredField } })} />
    </form>
  );
}

2) When a constraint is configured with a primitive value, then only the field's constraint value is configured. When the constraint is broken, the browser's default error message for that constraint will be displayed.

This syntax only exists for convenience. You are free to use the regular HTML attributes instead if you like.

import { createFormValidityObserver } from "@form-observer/solid";
import type { ValidatableField } from "@form-observer/solid";

function MyForm() {
  const { autoObserve, configure } = createFormValidityObserver("focusout");
  const requiredField = (field: ValidatableField) => `${field.labels[0]?.textContent ?? "Field"} is required.`;

  return (
    <form use:autoObserve>
      {/* Note: Accessible <label>s and error containers were omitted from this example. */}
      <input {...configure("email-1", { required: requiredField, type: "email" })} />
      <input {...configure("email-2", { required: requiredField })} type="email" />
      <input name="email-3" type="email" required />
    </form>
  );
}
The SolidFieldProps Return Type of configure

The return type of configure is simply an object containing the props that should be applied to the configured field. In addition to the field's name, this object will include any validation props that were configured by the function (e.g., required, min, pattern, etc.).

JSX Support

The SolidFormValidityObserver replaces the default error renderer with a function that can render Solid JSX or HTML Template Strings to the DOM.

import { createFormValidityObserver } from "@form-observer/solid";

function MyForm() {
  const { autoObserve, configure } = createFormValidityObserver("focusout");
  // Other Setup ...

  return (
    <>
      <form id="example" use:autoObserve>
        {/* Other Internal Fields ... */}

        <label for="password">Password</label>
        <input
          id="password"
          type="password"
          aria-describedby="password-error"
          {...configure("password", {
            pattern: {
              value: "SOME_VALID_REGEX",
              render: true,
              message: (input: HTMLInputElement) => (
                <ul>
                  <li data-use-red-text={!/\d/.test(input.value)}>Password must include at least 1 number</li>
                  <li data-use-red-text={!/[a-zA-Z]/.test(input.value)}>Password must include at least 1 letter</li>
                  {/* Other Requirements ... */}
                </ul>
              ),
            },
          })}
        />
        <div id="password-error" />

        <label for="complaints">Complaints</label>
        <textarea
          id="complaints"
          aria-describedby="complaints-error"
          {...configure("complaints", { minlength: { value: 300, message: "<p>Come on! Give us a REAL rant!</p>" } })}
        />
        <div id="complaints-error" />
      </form>

      {/* External Fields */}
    </>
  );
}

Note: We recommend using JSX whenever you need to render error messages to the DOM as HTML because doing so makes it easier to write valid, formattable markup. HTML Template Strings are only supported because the core FormValidityObserver supports them.