colinhacks / zod

TypeScript-first schema validation with static type inference
https://zod.dev
MIT License
33.09k stars 1.15k forks source link

question: How can I show fielderrors as hints? #3736

Open gregorvoinov opened 2 weeks ago

gregorvoinov commented 2 weeks ago

Hi,

I have a password field with many rules. The rules should be displayed as hints below the textfield to help the user to get it right in the first instance.

what I have done now was validating the schema with an empty string to get all possible errors for that field

const schema = z.object({
  firstname: z.string().min(3),
  lastname: z.string().min(3),
  email: z.string().email("Invalid email"),
  password: z
    .string()
    .min(8)
    .refine((password) => /[A-Z]/.test(password), {
      message: "Add one uppercase",
    })
    .refine((password) => /[0-9]/.test(password), {
      message: "Add one number",
    })
    .refine(
      (password) => /[`!@#$%^&*()_\-+=§\[\]{};':"\\|,.<>\/?~ ]/.test(password),
      {
        message: "Add one special character",
      }
    ),
});

// get all errors for password field
const result = schema.safeParse({ password: "" });
const passwordHints = result.error.flatten().fieldErrors.password;

is this the way to go or there are other solutions to get the errors?


This is the rest of my code. not really a question. just asking for feedback if this is ok or there are other ways to solve this with Zod....

To update the rules, I created a function where I match the list of all errors with the current validated error

//passwordHints enriched with states if rule is fulfilled
const passwordHintsWithStates = ref();

//live validation
const onInput = (e) => {
  passwordStrength(e.target.value);
};
//check password rules
const passwordStrength = (value) => {
  let result = schema.safeParse({ password: value });
  result = result.error.flatten().fieldErrors.password;

  passwordHintsWithStates.value = passwordHints.map((hint) => {
    let error = false;
    if (result) {
      error = result.includes(hint);
    }
    return {
      message: hint,
      error: error,
      init: init.value,
    };
  });
};

//init state used to display hints in gray
const init = ref(true);
const onBlur = (e) => {
  init.value = false;
  passwordStrength(e.target.value);
};
<div class="flex flex-col gap-1 mt-2">
    <span
        v-for="hint in passwordHintsWithStates"
        :class="
          hint.init && hint.error
            ? 'text-gray-400'
            : hint.init && !hint.error
            ? 'text-success-base'
            : hint.error
            ? 'text-error-base'
            : 'text-success-base'
        "
    > {{ hint.message }}</span>
</div>

any feedback is highly appreciated

cheers, gregor