fabian-hiller / modular-forms

The modular and type-safe form library for SolidJS, Qwik and Preact
https://modularforms.dev
MIT License
988 stars 53 forks source link

How to validate a specific field when input changes (Solid) #116

Open TiLopes opened 1 year ago

TiLopes commented 1 year ago

I currently have a password field which is validated using refine from zod. But when the user first starts entering the password it is not validated and only on submit the validation starts. I know validate() exists but when I'm styling the input to show the errors I don't want the page to be loaded and the field already showing errors without the user even interacting with the field.

So how could I force the validation to be run onInput?

relevant code:

<Field name="password">
        {(field, props) => (
          <TextField.Root class="flex flex-col gap-1">
            <TextField.Label>Palavra-chave</TextField.Label>
            <TextField.Input
              {...props}
              type="password"
              value={field.value}
              class="p-2 border rounded outline-offset-2"
              classList={{
                'border-red-500': field.error !== '',
                'border-green-700': field.error === '' && field.value.length > 0,
              }}
            ></TextField.Input>
            {field.error && <span class="text-xs text-red-500 font-medium">{field.error}</span>}
            <Show when={field.error}>
              <ul class="text-sm w-full flex flex-wrap text-slate-600">
                <li class="mx-6 before:text-blue-500 list-dot before:w-[1em] before:inline-block before:-ml-2 before:text-lg">
                  8 caracteres
                </li>
                <li class="mx-6 before:text-blue-500 list-dot before:w-[1em] before:inline-block before:-ml-2 before:text-lg">
                  1 número
                </li>
                <li class="mx-6 before:text-blue-500 list-dot before:w-[1em] before:inline-block before:-ml-2 before:text-lg">
                  Letra maiúscula
                </li>
                <li class="mx-6 before:text-blue-500 list-dot before:w-[1em] before:inline-block before:-ml-2 before:text-lg">
                  Letra minúscula
                </li>
                <li class="mx-6 before:text-blue-500 list-dot before:w-[1em] before:inline-block before:-ml-2 before:text-lg">
                  Caractere especial
                </li>
              </ul>
            </Show>
          </TextField.Root>
        )}
      </Field>
const validarPassword = (password: string) => {
  if (password.length < 8) {
    return false;
  }

  if (password.toUpperCase() === password) {
    return false;
  }

  if (password.toLowerCase() === password) {
    return false;
  }

  if (!/\d/.test(password)) {
    return false;
  }

  if (!/[ `!@#$%^&*()_+\-=\]{};':"\\|,.<>?~]/.test(password)) {
    return false;
  }

  return true;
};

const UtilizadorSchema = z
  .object({
    nome: z.string().nonempty('Campo obrigatório'),
    email: z.string().nonempty('Campo obrigatório').email('Inválido'),
    nif: z
      .string()
      .nonempty('Campo obrigatório')
      .regex(/^\d{9}/, 'Inválido'),
    n_telemovel: z.string().nonempty('Campo obrigatório'),
    password: z
      .string()
      .nonempty('Campo obrigatório')
      .refine(validarPassword, () => ({
        message: 'Não cumpre os requisitos',
      })),
    confirmar_password: z.string().nonempty('Campo obrigatório'),
  })
  .superRefine(({ confirmar_password, password }, ctx) => {
    if (confirmar_password !== password) {
      ctx.addIssue({
        code: 'custom',
        message: 'As palavras-chave não são iguais',
        path: ['confirmar_password'],
      });
    }
  });
fabian-hiller commented 1 year ago

I think that the behavior you need can be controlled by the revalidateOn option of createForm: https://modularforms.dev/solid/api/createForm

TiLopes commented 1 year ago

Using revalidateOn and validateOn does work. Will there ever be the possibility of overriding when to validate per field? Since not all field might require to be validated when on change.

fabian-hiller commented 1 year ago

Currently, this is not planned. In most cases, validation is so fast that optimization is not necessarily required. Feel free to go into more detail about your use case so I understand why this feature might be useful.

TiLopes commented 1 year ago

Let's say I have a form with two fields: name and password. While on the name the only verification that has to be done is to check if it's not empty, I may want to show the minimum requirements of the password while the user is typing and if they are fulfilled I then hide the requirements. Even without the per-field validation there are no problems but I guess it might be a bit annoying seeing the error message even though you have not finished typing everything. This is more than anything a QoL feature since like you said validation is fast enough that there would be no slowdowns.

An example with email and password:

https://github.com/fabian-hiller/modular-forms/assets/106965010/8762e540-b355-452d-b948-a311c07f2fcc

I get the error in email as soon as I type which for some might be bothersome I guess.

fabian-hiller commented 1 year ago

I understand. Technically, it should be possible to add this feature. However, I don't have the time at the moment.

TiLopes commented 1 year ago

Feel free to do it whenever you think is the right time. I've recently started using Solid and this library helps a lot in managing forms, keep it up!