mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 12:14:41 +01:00
355 lines
8.3 KiB
Markdown
355 lines
8.3 KiB
Markdown
---
|
|
description: Collect and validate form data.
|
|
links:
|
|
- label: GitHub
|
|
icon: i-simple-icons-github
|
|
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/Form.vue
|
|
---
|
|
|
|
## 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 { ref } from 'vue'
|
|
import type { FormError, FormSubmitEvent } from '@nuxt/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
|
|
}
|
|
|
|
async function submit (event: FormSubmitEvent<any>) {
|
|
// Do something with data
|
|
console.log(event.data)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<UForm
|
|
:validate="validate"
|
|
:state="state"
|
|
@submit="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 { ref } from 'vue'
|
|
import { object, string, InferType } from 'yup'
|
|
import type { FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'
|
|
|
|
const schema = object({
|
|
email: string().email('Invalid email').required('Required'),
|
|
password: string()
|
|
.min(8, 'Must be at least 8 characters')
|
|
.required('Required')
|
|
})
|
|
|
|
type Schema = InferType<typeof schema>
|
|
|
|
const state = ref({
|
|
email: undefined,
|
|
password: undefined
|
|
})
|
|
|
|
async function submit (event: FormSubmitEvent<Schema>) {
|
|
// Do something with event.data
|
|
console.log(event.data)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<UForm
|
|
:schema="schema"
|
|
:state="state"
|
|
@submit="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 { ref } from 'vue'
|
|
import { z } from 'zod'
|
|
import type { FormSubmitEvent } from '@nuxt/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')
|
|
})
|
|
|
|
type Schema = z.output<typeof schema>
|
|
|
|
const state = ref({
|
|
email: undefined,
|
|
password: undefined
|
|
})
|
|
|
|
async function submit (event: FormSubmitEvent<Schema>) {
|
|
// Do something with data
|
|
console.log(event.data)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<UForm
|
|
:schema="schema"
|
|
:state="state"
|
|
@submit="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 { ref } from 'vue'
|
|
import Joi from 'joi'
|
|
import type { FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'
|
|
|
|
const schema = Joi.object({
|
|
email: Joi.string().required(),
|
|
password: Joi.string()
|
|
.min(8)
|
|
.required()
|
|
})
|
|
|
|
const state = ref({
|
|
email: undefined,
|
|
password: undefined
|
|
})
|
|
|
|
async function submit (event: FormSubmitEvent<any>) {
|
|
// Do something with event.data
|
|
console.log(event.data)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<UForm
|
|
:schema="schema"
|
|
:state="state"
|
|
@submit="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>
|
|
```
|
|
::
|
|
|
|
## 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>
|
|
```
|
|
|
|
## Backend validation
|
|
|
|
You can manually set errors after form submission if required. To do this, simply use the `form.setErrors` function to set the errors as needed.
|
|
|
|
```vue
|
|
<script setup lang="ts">
|
|
import type { FormError, FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'
|
|
|
|
const state = ref({
|
|
email: undefined,
|
|
password: undefined
|
|
})
|
|
|
|
const form = ref()
|
|
|
|
async function submit (event: FormSubmitEvent<any>) {
|
|
form.value.clear()
|
|
const response = await fetch('...')
|
|
|
|
if (!response.status === 422) {
|
|
const errors = await response.json()
|
|
form.value.setErrors(errors.map((err) => {
|
|
// Map validation errors to { path: string, message: string }
|
|
}))
|
|
} else {
|
|
// ...
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<UForm ref="form" :state="state" @submit="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>
|
|
```
|
|
|
|
## Input events
|
|
|
|
The Form component automatically triggers validation upon `submit`, `input`, `blur` or `change` events. This ensures that any errors are displayed as soon as the user interacts with the form elements. You can control when validation happens this using the `validate-on` prop.
|
|
|
|
::component-example
|
|
#default
|
|
:form-example-elements{class="space-y-4 w-60"}
|
|
::
|
|
|
|
## Props
|
|
|
|
:component-props
|
|
|
|
## API
|
|
|
|
::field-group
|
|
::field{name="validate (path?: string, opts: { silent?: boolean })" type="Promise<T>"}
|
|
Triggers form validation. Will raise any errors unless `opts.silent` is set to true.
|
|
::
|
|
::field{name="clear (path?: string)" type="void"}
|
|
Clears form errors associated with a specific path. If no path is provided, clears all form errors.
|
|
::
|
|
::field{name="getErrors (path?: string)" type="FormError[]"}
|
|
Retrieves form errors associated with a specific path. If no path is provided, returns all form errors.
|
|
::
|
|
::field{name="setErrors (errors: FormError[], path?: string)" type="void"}
|
|
Sets form errors for a given path. If no path is provided, overrides all errors.
|
|
::
|
|
::field{name="errors" type="Ref<FormError[]>"}
|
|
A reference to the array containing validation errors. Use this to access or manipulate the error information.
|
|
::
|
|
::
|