Cookbook: register page
A complete sign-up form: cross-field validation, a must-accept checkbox, and a dialog awaited before the form resets.
Register
examples/cookbook/Register.vue<template>
<Form
class="mx-auto max-w-md"
@submit="onSubmit"
>
<FormFields :options="fields" />
<Button
type="submit"
class="mt-6"
block
>
Register
</Button>
</Form>
</template>
<script lang="ts" setup>
const schema = v.pipe(
v.object({
username: v.pipe(v.string(), v.nonEmpty(), v.minLength(3)),
email: v.pipe(v.string(), v.nonEmpty(), v.email()),
password: v.pipe(v.string(), v.minLength(8, 'Password must be at least 8 characters long.')),
confirmPassword: v.pipe(v.string(), v.nonEmpty('Please confirm your password.')),
acceptTerms: v.pipe(
v.boolean(),
v.check((accepted) => accepted, 'You must accept the terms.')
),
}),
v.forward(
v.check((input) => input.password === input.confirmPassword, 'The two passwords do not match.'),
['confirmPassword']
)
)
const form = useForm({
validationSchema: toTypedSchema(schema),
initialValues: {
username: '',
email: '',
password: '',
confirmPassword: '',
acceptTerms: false,
},
})
const fields = createFormFields(() => [
{
type: INPUT_TYPES.TEXT,
props: {
name: 'username',
label: 'Username',
leadingIcon: 'ph:user',
required: true,
},
},
{
type: INPUT_TYPES.TEXT,
props: {
name: 'email',
label: 'Email',
leadingIcon: 'ph:envelope',
required: true,
},
},
{
type: INPUT_TYPES.PASSWORD,
props: {
name: 'password',
label: 'Password',
required: true,
},
},
{
type: INPUT_TYPES.PASSWORD,
props: {
name: 'confirmPassword',
label: 'Confirm password',
required: true,
},
},
{
type: INPUT_TYPES.CHECKBOX,
props: {
name: 'acceptTerms',
label: 'I accept the terms and conditions',
},
},
])
const dialog = useDialog()
const onSubmit = form.handleSubmit(async (values) => {
await dialog.success({
title: `Welcome, ${values.username}!`,
description: 'Your account was created.',
})
form.resetForm()
}, moveToError)
defineExpose({
debug: computed(() => ({
values: form.values,
errors: form.errors.value,
})),
})
</script>