mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 20:19:34 +01:00
326 lines
6.9 KiB
Markdown
326 lines
6.9 KiB
Markdown
---
|
|
description: Collect and validate form data.
|
|
links:
|
|
- label: GitHub
|
|
icon: i-simple-icons-github
|
|
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/forms/Form.ts
|
|
navigation:
|
|
badge: Edge
|
|
---
|
|
|
|
## Usage
|
|
|
|
Use the Form component to validate form data using schema libraries such as [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/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
|
|
```vue
|
|
<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](#yup), [Zod](#zod) or [Joi](#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
|
|
```vue
|
|
<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
|
|
```vue
|
|
<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
|
|
```vue
|
|
<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.
|
|
|
|
```vue
|
|
<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: Partial<Schema> = ref({
|
|
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: Schema = 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](https://github.com/vuelidate/vuelidate):
|
|
|
|
```vue
|
|
<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
|