chore: update ProfileForm.vue and AccountForm

fix vee-validate initialValues on components with `componentField` slotProp
This commit is contained in:
sadeghbarati 2023-09-27 16:00:58 +03:30
parent 72ef22de60
commit 6be33b10fd
2 changed files with 119 additions and 106 deletions

View File

@ -83,32 +83,29 @@ async function handleSubmit(values: any) {
<Form <Form
v-slot="{ v-slot="{
setValues, setValues,
}" :validation-schema="accountFormSchema" class="space-y-8" }" :validation-schema="accountFormSchema" class="space-y-8" @submit="handleSubmit"
@submit="handleSubmit"
> >
<FormField v-slot="{ field }" name="name"> <FormField v-slot="{ componentField }" name="name">
<FormItem> <FormItem>
<FormLabel>Name</FormLabel> <FormLabel>Name</FormLabel>
<FormControl> <FormControl>
<Input type="text" placeholder="Your name" v-bind="field" /> <Input type="text" placeholder="Your name" v-bind="componentField" />
</FormControl> </FormControl>
<FormDescription> <FormDescription>
This is your public display name. It can be your real name or a This is the name that will be displayed on your profile and in emails.
pseudonym. You can only change this once every 30 days.
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
</FormField> </FormField>
<FormField v-slot="{ field, value }" name="dob"> <FormField v-slot="{ componentField, value }" name="dob">
<FormItem> <FormItem>
<FormLabel>Date of birth</FormLabel> <FormLabel>Date of birth</FormLabel>
<Popover> <Popover>
<PopoverTrigger as-child> <PopoverTrigger as-child>
<FormControl> <FormControl>
<Button <Button
variant="outline" variant="outline" :class="cn(
:class="cn(
'w-[280px] pl-3 text-left font-normal', 'w-[280px] pl-3 text-left font-normal',
!value && 'text-muted-foreground', !value && 'text-muted-foreground',
)" )"
@ -119,7 +116,7 @@ async function handleSubmit(values: any) {
</FormControl> </FormControl>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent class="p-0"> <PopoverContent class="p-0">
<Calendar v-bind="field" /> <Calendar v-bind="componentField" />
</PopoverContent> </PopoverContent>
</Popover> </Popover>
<FormDescription> <FormDescription>
@ -137,10 +134,7 @@ async function handleSubmit(values: any) {
<PopoverTrigger as-child> <PopoverTrigger as-child>
<FormControl> <FormControl>
<Button <Button
variant="outline" variant="outline" role="combobox" :aria-expanded="open" :class="cn(
role="combobox"
:aria-expanded="open"
:class="cn(
'w-[200px] justify-between', 'w-[200px] justify-between',
!value && 'text-muted-foreground', !value && 'text-muted-foreground',
)" )"
@ -159,10 +153,7 @@ async function handleSubmit(values: any) {
<CommandEmpty>No framework found.</CommandEmpty> <CommandEmpty>No framework found.</CommandEmpty>
<CommandGroup> <CommandGroup>
<CommandItem <CommandItem
v-for="language in languages" v-for="language in languages" :key="language.value" :value="language.value" @select="(val) => {
:key="language.value"
:value="language.value"
@select="(val) => {
setValues({ language: val }) setValues({ language: val })
open = false open = false
}" }"

View File

@ -1,10 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { FieldArray, useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
import * as z from 'zod' import * as z from 'zod'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { Input } from '@/lib/registry/new-york/ui/input' import { Input } from '@/lib/registry/new-york/ui/input'
import { Label } from '@/lib/registry/new-york/ui/label' import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/lib/registry/default/ui/form'
import { Separator } from '@/lib/registry/new-york/ui/separator' import { Separator } from '@/lib/registry/new-york/ui/separator'
import { Textarea } from '@/lib/registry/new-york/ui/textarea' import { Textarea } from '@/lib/registry/new-york/ui/textarea'
import { import {
@ -19,17 +21,7 @@ import { Button } from '@/lib/registry/new-york/ui/button'
const verifiedEmails = ref(['m@example.com', 'm@google.com', 'm@support.com']) const verifiedEmails = ref(['m@example.com', 'm@google.com', 'm@support.com'])
const profileForm = ref({ const profileFormSchema = toTypedSchema(z.object({
username: '',
email: '',
bio: 'I own a computer.',
urls: [
{ value: 'https://shadcn.com' },
{ value: 'http://twitter.com/shadcn' },
],
})
const profileFormSchema = z.object({
username: z username: z
.string() .string()
.min(2, { .min(2, {
@ -51,20 +43,22 @@ const profileFormSchema = z.object({
}), }),
) )
.optional(), .optional(),
}))
const { handleSubmit, resetForm } = useForm({
validationSchema: profileFormSchema,
initialValues: {
bio: 'I own a computer.',
urls: [
{ value: 'https://shadcn.com' },
{ value: 'http://twitter.com/shadcn' },
],
},
}) })
type ProfileFormValues = z.infer<typeof profileFormSchema> const onSubmit = handleSubmit((values, { resetForm }) => {
const errors = ref<z.ZodFormattedError<ProfileFormValues> | null>(null) console.log(values)
})
async function handleSubmit() {
const result = profileFormSchema.safeParse(profileForm.value)
if (!result.success) {
errors.value = result.error.format()
return
}
errors.value = null
console.log('Form submitted!')
}
</script> </script>
<template> <template>
@ -77,75 +71,103 @@ async function handleSubmit() {
</p> </p>
</div> </div>
<Separator /> <Separator />
<form class="space-y-8" @submit.prevent="handleSubmit"> <form class="space-y-8" @submit="onSubmit">
<div class="grid gap-2"> <FormField v-slot="{ field }" name="username">
<Label for="username" :class="cn('text-sm', errors?.username && 'text-destructive')"> <FormItem>
Username <FormLabel>Username</FormLabel>
</Label> <FormControl>
<Input id="username" v-model="profileForm.username" placeholder="shadcn" /> <Input type="text" placeholder="shadcn" v-bind="field" />
<span class="text-muted-foreground text-sm"> </FormControl>
This is your public display name. It can be your real name or a pseudonym. You can only change this once every 30 days. <FormDescription>
</span> This is your public display name. It can be your real name or a pseudonym. You can only change this once every 30 days.
<div v-if="errors?.username" class="text-sm text-destructive"> </FormDescription>
<span v-for="error in errors.username._errors" :key="error">{{ error }}</span> <FormMessage />
</div> </FormItem>
</FormField>
<FormField v-slot="{ componentField }" name="email">
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Select v-bind="componentField">
<SelectTrigger>
<SelectValue placeholder="Select an email" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem v-for="email in verifiedEmails" :key="email" :value="email">
{{ email }}
</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</FormControl>
<FormDescription>
You can manage verified email addresses in your email settings.
</FormDescription>
<FormMessage />
</FormItem>
</FormField>
<FormField v-slot="{ componentField }" name="bio">
<FormItem>
<FormLabel>Bio</FormLabel>
<FormControl>
<Textarea placeholder="Tell us a little bit about yourself" v-bind="componentField" />
</FormControl>
<FormDescription>
You can <span>@mention</span> other users and organizations to link to them.
</FormDescription>
<FormMessage />
</FormItem>
</FormField>
<div>
<FieldArray v-slot="{ fields, push }" name="urls">
<div v-for="(fieldq, index) in fields" :key="`urls-${fieldq.key}`">
<!-- <button type="button" @click="remove(index)">
Remove
</button> -->
<FormField v-slot="{ componentField }" :name="`urls[${index}].value`">
<FormItem>
<FormLabel :class="cn(index !== 0 && 'sr-only')">
URLs
</FormLabel>
<FormDescription :class="cn(index !== 0 && 'sr-only')">
Add links to your website, blog, or social media profiles.
</FormDescription>
<FormControl>
<Input type="url" v-bind="componentField" />
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</div>
<Button
type="button"
variant="outline"
size="sm"
class="text-xs w-20 mt-2"
@click="push({ value: '' })"
>
Add URL
</Button>
</FieldArray>
</div> </div>
<div class="grid gap-2">
<Label for="email" :class="cn('text-sm', errors?.email && 'text-destructive')"> <div class="flex gap-2 justify-start">
Email <Button type="submit">
</Label> Update profile
<Select id="email" v-model="profileForm.email"> </Button>
<SelectTrigger>
<SelectValue placeholder="Select an email" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem v-for="email in verifiedEmails" :key="email" :value="email">
{{ email }}
</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<span class="text-muted-foreground text-sm">
You can manage verified email addresses in your email settings.
</span>
<div v-if="errors?.email" class="text-sm text-destructive">
<span v-for="error in errors.email._errors" :key="error">{{ error }}</span>
</div>
</div>
<div class="grid gap-2">
<Label for="bio" :class="cn('text-sm', errors?.bio && 'text-destructive')">
Bio
</Label>
<Textarea id="bio" v-model="profileForm.bio" placeholder="Tell us about yourself." />
<span class="text-muted-foreground text-sm">
You can @mention other users and organizations to link to them.
</span>
<div v-if="errors?.bio" class="text-sm text-destructive">
<span v-for="error in errors.bio._errors" :key="error">{{ error }}</span>
</div>
</div>
<div class="grid gap-2">
<Label for="urls" :class="cn('text-sm', errors?.urls && 'text-destructive')">
URLs
</Label>
<Input v-for="(url, index) in profileForm.urls" id="urls" :key="index" v-model="url.value" />
<div v-if="errors?.urls" class="text-sm text-destructive">
<span v-for="error in errors.urls._errors" :key="error">{{ error }}</span>
</div>
<Button <Button
type="button" type="button"
variant="outline" variant="outline"
size="sm" @click="resetForm"
class="text-xs w-20 mt-2"
@click="profileForm.urls?.push({ value: '' })"
> >
Add URL Reset form
</Button>
</div>
<div class="flex justify-start">
<Button type="submit">
Update profile
</Button> </Button>
</div> </div>
</form> </form>