kawa
kawa

Reputation: 611

On svelte Superform, how to display error message without using +page.server.ts

I am using shadcn-svelte form for my Svelte application. Currently, I am trying to validate my form data client-side using Zod and display the error messages below the input fields. However, I'm not sure if my current approach is the best way to handle errors.

Here is my schema:

some-page/schema.ts

import { z } from "zod";
 
export const formSchema = z.object({
  email: z.string().min(2).max(50).toLowerCase(),
  password: z.string().min(3).toUpperCase()
});
 
export type FormSchema = typeof formSche

some-page/+page.svelte

<script lang="ts">
  import * as Form from "$lib/components/ui/form";
  import { Input } from "$lib/components/ui/input";
  import { formSchema, type FormSchema } from "./download-schema";
  import SuperDebug, {
    type SuperValidated,
    type Infer,
    superForm,
  } from "sveltekit-superforms";
  import { zodClient } from "sveltekit-superforms/adapters";
 
  export let data: SuperValidated<Infer<FormSchema>>;
 
  const form = superForm(data, {
    validators: zodClient(formSchema)
  });
 
  const { form: formData, constraints, errors } = form;


  function onSubmit() {
  try {
    formSchema.parse($formData);
    console.log("Validation successful!");
  } catch (error: any) {
    console.error("Validation failed:");
    error.errors.forEach((err: any) => {
      console.error(`Path: ${err.path.join('.')}, Message: ${err.message}`);
      // for each error
      console.error(err.path.join('.'))
      errors.set({
          [err.path.join('.')]: err.message
      })
    });
  }
}
</script>
 
<SuperDebug data={$formData}/>
<form on:submit={onSubmit}>
    <Form.Field {form} name="email">
    <Form.Control let:attrs>
      <Form.Label>Email</Form.Label>
      <Input type="email" {...attrs} bind:value={$formData.email} {...$constraints.email}/>
    </Form.Control>
    <Form.Description>Email.</Form.Description>
    <Form.FieldErrors />
  </Form.Field>

  <Form.Field {form} name="password">
    <Form.Control let:attrs>
      <Form.Label>Password</Form.Label>
      <Input type="password" {...attrs} bind:value={$formData.password} {...$constraints.password}/>
    </Form.Control>
    <Form.Description>Password</Form.Description>
    <Form.FieldErrors />
  </Form.Field>
  <Form.Button type="submit">Download</Form.Button>
</form>

Issue

I am able to catch validation errors and log them in the console, but afterall, I need to display error on UI. I am not sure if setting errors in the errors store like this is the correct way to handle client-side error setting. The errors are not showing up below the input fields as expected.

Question

How should I properly handle and display validation errors below the respective input fields without using server-side code? Is there a recommended way to manage and show error messages using shadcn-svelte forms and Zod?

Upvotes: 0

Views: 1013

Answers (1)

kawa
kawa

Reputation: 611

This works

<script lang="ts">
  import * as Form from "$lib/components/ui/form";
  import { Input } from "$lib/components/ui/input";

  import {
    type SuperValidated,
    type Infer,
    superForm,
  } from "sveltekit-superforms";
  import { zodClient } from "sveltekit-superforms/adapters";
  import { formSchema, type FormSchema } from "./download-schema";

  export let data: SuperValidated<Infer<FormSchema>>;

  const form = superForm(data, {
    validators: zodClient(formSchema),
  });

  const { form: formData, constraints, errors, validateForm, reset } = form;

  const handleSubmit = async () => {
    const result = await validateForm();
    if (!result.valid) {
      errors.update((v) => {
        return {
          ...v,
          email: result.errors.email,
          password: result.errors.password,
        };
      });

      return;
    }
    console.log($formData);
    errors.clear()
    reset()
    console.log($errors)
  };
</script>

<form>
  <Form.Field {form} name="email">
    <Form.Control let:attrs>
      <Form.Label>Email</Form.Label>
      <Input {...attrs} bind:value={$formData.email} {...$constraints.email} />
    </Form.Control>
    <Form.Description>This is your public display name.</Form.Description>
    <Form.FieldErrors />
  </Form.Field>

  <Form.Field {form} name="password">
    <Form.Control let:attrs>
      <Form.Label>Password</Form.Label>
      <Input type="password" {...attrs} bind:value={$formData.password} />
    </Form.Control>
    <Form.Description>This is your public display name.</Form.Description>
    <Form.FieldErrors />
  </Form.Field>
  <Form.Button on:click={handleSubmit}>Submit</Form.Button>
</form>


Upvotes: 1

Related Questions