Files
ui/docs/content/3.forms/10.form.md
Paul Grau 3d6839da97 fix(Form): fix wrong type of validate (#496)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2023-09-07 15:13:47 +02:00

6.9 KiB

description, links, navigation
description links navigation
Collect and validate form data.
label icon to
GitHub i-simple-icons-github https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/forms/Form.ts
badge
New

Usage

Use the Form component to validate form data using schema libraries such as Yup, Zod, Joi or your own validation logic. It works seamlessly with the FormGroup component to automatically display error messages around form elements.

The Form component requires the validate and state props for form validation.

  • state - a reactive object that holds the current state of the form.
  • validate - a function that takes the form's state as input and returns an array of FormError objects with the following fields:
    • message - the error message to display.
    • path - the path to the form element matching the name.

::component-example #default :form-example-basic{class="space-y-4 w-60"}

#code

<script setup lang="ts">
import type { FormError } from '@nuxthq/ui/dist/runtime/types'

const state = ref({
  email: undefined,
  password: undefined
})

const validate = (state: any): FormError[] => {
  const errors = []
  if (!state.email) errors.push({ path: 'email', message: 'Required' })
  if (!state.password) errors.push({ path: 'password', message: 'Required' })
  return errors
}

const form = ref()

async function submit () {
  await form.value!.validate()
  // Do something with state.value
}
</script>

<template>
  <UForm
    ref="form"
    :validate="validate"
    :state="state"
    @submit.prevent="submit"
  >
    <UFormGroup label="Email" name="email">
      <UInput v-model="state.email" />
    </UFormGroup>

    <UFormGroup label="Password" name="password">
      <UInput v-model="state.password" type="password" />
    </UFormGroup>

    <UButton type="submit">
      Submit
    </UButton>
  </UForm>
</template>

::

Schema

You can provide a schema from Yup, Zod or Joi through the schema prop to validate the state. It's important to note that none of these libraries are included by default, so make sure to install the one you need.

Yup

::component-example #default :form-example-yup{class="space-y-4 w-60"}

#code

<script setup lang="ts">
import { object, string, InferType } from 'yup'

const schema = object({
  email: string().email('Invalid email').required('Required'),
  password: string()
    .min(8, 'Must be at least 8 characters')
    .required('Required')
})

const state = ref({
  email: undefined,
  password: undefined
})

const form = ref()

async function submit () {
  await form.value!.validate()
  // Do something with state.value
}
</script>

<template>
  <UForm
    ref="form"
    :schema="schema"
    :state="state"
    @submit.prevent="submit"
  >
    <UFormGroup label="Email" name="email">
      <UInput v-model="state.email" />
    </UFormGroup>

    <UFormGroup label="Password" name="password">
      <UInput v-model="state.password" type="password" />
    </UFormGroup>

    <UButton type="submit">
      Submit
    </UButton>
  </UForm>
</template>

::

Zod

::component-example #default :form-example-zod{class="space-y-4 w-60"}

#code

<script setup lang="ts">
import { z } from 'zod'

const schema = z.object({
  email: z.string().email('Invalid email'),
  password: z.string().min(8, 'Must be at least 8 characters')
})

const state = ref({
  email: undefined,
  password: undefined
})

const form = ref()

async function submit () {
  await form.value!.validate()
  // Do something with state.value
}
</script>

<template>
  <UForm
    ref="form"
    :schema="schema"
    :state="state"
    @submit.prevent="submit"
  >
    <UFormGroup label="Email" name="email">
      <UInput v-model="state.email" />
    </UFormGroup>

    <UFormGroup label="Password" name="password">
      <UInput v-model="state.password" type="password" />
    </UFormGroup>

    <UButton type="submit">
      Submit
    </UButton>
  </UForm>
</template>

::

Joi

::component-example #default :form-example-joi{class="space-y-4 w-60"}

#code

<script setup lang="ts">
import Joi from 'joi'
import type { Schema } from 'joi'

const schema = Joi.object({
  email: Joi.string().required(),
  password: Joi.string()
    .min(8)
    .required()
})

const state = ref({
  email: undefined,
  password: undefined
})

async function submit () {
  await form.value!.validate()
  // Do something with state.value
}
</script>

<template>
  <UForm
    ref="form"
    :schema="schema"
    :state="state"
    @submit.prevent="submit"
  >
    <UFormGroup label="Email" name="email">
      <UInput v-model="state.email" />
    </UFormGroup>

    <UFormGroup label="Password" name="password">
      <UInput v-model="state.password" type="password" />
    </UFormGroup>

    <UButton type="submit">
      Submit
    </UButton>
  </UForm>
</template>

::

Type Inference

You can utilize Zod and Yup's type inference feature to automatically infer the types of your schema and form data. This allows for strong type checking and better code validation, reducing the likelihood of errors.

<script setup lang="ts">
import type { Form } from '@nuxthq/ui/dist/runtime/types'

// [...]
const schema = z.object({
  email: z.string().email('Invalid email'),
  password: z.string().min(8, 'Must be at least 8 characters')
})

const state = ref<Partial<Schema>>({
  email: undefined,
  password: undefined
})

type Schema = z.output<typeof schema>
// For Yup, use:
// type Schema = InferType<typeof schema>
const form = ref<Form<Schema>>()

async function submit() {
  const data = await form.value!.validate()
  // Do something with data
}
</script>
// [...]

Other libraries

For other validation libraries, you can define your own component with custom validation logic.

Here is an example with Vuelidate:

<script setup lang="ts">
import useVuelidate from '@vuelidate/core';

const props = defineProps({
  rules: { type: Object, required: true },
  model: { type: Object, required: true },
});

const form = ref();
const v = useVuelidate(props.rules, props.model);

async function validateWithVuelidate() {
  v.value.$touch();
  await v.value.$validate();
  return v.value.$errors.map((error) => ({
    message: error.$message,
    path: error.$propertyPath,
  }));
}

defineExpose({
  validate: async () => {
    await form.value.validate();
  },
});
</script>

<template>
  <UForm ref="form" :model="model" :validate="validateWithVuelidate">
    <slot />
  </UForm>
</template>

Input events

The Form component automatically triggers validation upon input blur or change events. This ensures that any errors are displayed as soon as the user interacts with the form elements.

::component-example #default :form-example-elements{class="space-y-4 w-60"} ::

Props

:component-props