Update form pages

pull/6705/head
Mert Sincan 2024-11-01 06:00:00 +00:00
parent 204c823cbb
commit 0432047fd4
11 changed files with 748 additions and 170 deletions

View File

@ -0,0 +1,39 @@
<template>
<DocSectionText v-bind="$attrs">
<p>
The <i>FormField</i> is a helper component that provides validation and tracking for form fields, offering a more flexible structure to bind PrimeVue, non-PrimeVue components or HTML elements to Form component. Additionally, with props
like <i>validateOn*</i>, <i>initialValue</i>, <i>resolver</i>, and <i>name</i>, Form behaviors can be controlled directly from this component.
</p>
</DocSectionText>
<DocSectionCode :code="code" hideToggleCode importCode hideStackBlitz />
<BuiltInDoc />
<NonPrimeVueDoc />
<ResolverDoc />
<TemplateDoc />
</template>
<script>
import { markRaw } from 'vue';
import BuiltInDoc from './formfield/BuiltInDoc.vue';
import NonPrimeVueDoc from './formfield/NonPrimeVueDoc.vue';
import ResolverDoc from './formfield/ResolverDoc.vue';
import TemplateDoc from './formfield/TemplateDoc.vue';
export default {
data() {
return {
code: {
basic: `
import { FormField } from '@primevue/forms';
`
}
};
},
components: {
BuiltInDoc: markRaw(BuiltInDoc),
NonPrimeVueDoc: markRaw(NonPrimeVueDoc),
ResolverDoc: markRaw(ResolverDoc),
TemplateDoc: markRaw(TemplateDoc)
}
};
</script>

View File

@ -11,7 +11,7 @@ export default {
return {
code: {
basic: `
import Form from '@primevue/forms';
import { Form } from '@primevue/forms';
`
}
};

View File

@ -1,159 +0,0 @@
<template>
<DocSectionText v-bind="$attrs">
<p>
The <i>register</i> callback allows non-PrimeVue components to be registered within Form component. This enables custom elements to participate in the form's validation and value tracking, ensuring they work alongside PrimeVue components.
</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<input type="text" placeholder="Username" v-bind="$form.register('username')" />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/forms/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
username: ''
},
resolver: zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' })
})
),
code: {
basic: `
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<input type="text" placeholder="Username" v-bind="$form.register('username')" />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Toast />
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<input type="text" placeholder="Username" v-bind="$form.register('username')" />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/forms/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
username: ''
},
resolver: zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' })
})
)
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
<\/script>
<style scoped>
input {
width: 100%;
color: var(--p-inputtext-color);
background: var(--p-inputtext-background);
border: 1px solid var(--p-inputtext-border-color);
}
<\/style>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<input type="text" placeholder="Username" v-bind="$form.register('username')" />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script setup>
import { reactive } from 'vue';
import { zodResolver } from '@primevue/forms/resolvers';
import { z } from 'zod';
import { useToast } from 'primevue/usetoast';
const toast = useToast();
const initialValues = reactive({
username: ''
});
const resolver = zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' })
})
);
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
<style scoped>
input {
width: 100%;
color: var(--p-inputtext-color);
background: var(--p-inputtext-background);
border: 1px solid var(--p-inputtext-border-color);
}
<\/style>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>
<style scoped>
input {
width: 100%;
color: var(--p-inputtext-color);
background: var(--p-inputtext-background);
border: 1px solid var(--p-inputtext-border-color);
}
</style>

View File

@ -3,7 +3,7 @@
</template>
<script setup>
import * as PrimeVue from 'primevue/primevue';
import * as PrimeVue from 'primevue';
import { computed, inject } from 'vue';
const props = defineProps({

View File

@ -1,7 +1,7 @@
<template>
<div class="flex flex-col gap-2">
<FormField class="flex flex-col gap-2">
<slot />
</div>
</FormField>
</template>
<script setup>

View File

@ -0,0 +1,123 @@
<template>
<DocSectionText label="Built-in" :level="2" v-bind="$attrs">
<p>It can be easily integrated with PrimeVue built-in components by wrapping them inside the FormField and using its props to manage validation and state.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<FormField v-slot="$field" name="username" initialValue="" class="flex flex-col gap-2">
<InputText type="text" placeholder="Username" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/forms/resolvers';
import { z } from 'zod';
export default {
data() {
return {
resolver: zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' })
})
),
code: {
basic: `
<Form :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<FormField v-slot="$field" name="username" initialValue="" class="flex flex-col gap-2">
<InputText type="text" placeholder="Username" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Toast />
<Form :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<FormField v-slot="$field" name="username" initialValue="" class="flex flex-col gap-2">
<InputText type="text" placeholder="Username" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/forms/resolvers';
import { z } from 'zod';
export default {
data() {
return {
resolver: zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' })
})
)
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<FormField v-slot="$field" name="username" initialValue="" class="flex flex-col gap-2">
<InputText type="text" placeholder="Username" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script setup>
import { reactive } from 'vue';
import { zodResolver } from '@primevue/forms/resolvers';
import { z } from 'zod';
import { useToast } from 'primevue/usetoast';
const toast = useToast();
const resolver = zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' })
})
);
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,178 @@
<template>
<DocSectionText label="Non-PrimeVue" :level="2" v-bind="$attrs">
<p>It can also be used with non-PrimeVue components, providing a flexible way to manage validation and state for any custom HTML elements or third-party libraries.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<FormField v-slot="$field" name="username" initialValue="" class="flex flex-col gap-2">
<input type="text" placeholder="Username" :class="[{ error: $field?.invalid }]" v-bind="$field.props" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="password" initialValue="PrimeVue" class="flex flex-col gap-2">
<input v-model="$field.value" type="password" placeholder="Password" :class="[{ error: $field?.invalid }]" @input="$field.onInput" @blur="$field.onBlur" @change="$field.onChange" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/forms/resolvers';
import { z } from 'zod';
export default {
data() {
return {
resolver: zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' }),
password: z.string().min(1, { message: 'Password is required.' })
})
),
code: {
basic: `
<Form :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<FormField v-slot="$field" name="username" initialValue="" class="flex flex-col gap-2">
<input type="text" placeholder="Username" :class="[{ error: $field?.invalid }]" v-bind="$field.props" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="password" initialValue="PrimeVue" class="flex flex-col gap-2">
<input v-model="$field.value" type="password" placeholder="Password" :class="[{ error: $field?.invalid }]" @input="$field.onInput" @blur="$field.onBlur" @change="$field.onChange" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Toast />
<Form :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<FormField v-slot="$field" name="username" initialValue="" class="flex flex-col gap-2">
<input type="text" placeholder="Username" :class="[{ error: $field?.invalid }]" v-bind="$field.props" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="password" initialValue="PrimeVue" class="flex flex-col gap-2">
<input v-model="$field.value" type="password" placeholder="Password" :class="[{ error: $field?.invalid }]" @input="$field.onInput" @blur="$field.onBlur" @change="$field.onChange" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/forms/resolvers';
import { z } from 'zod';
export default {
data() {
return {
resolver: zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' }),
password: z.string().min(1, { message: 'Password is required.' })
})
)
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
<\/script>
<style scoped>
input {
width: 100%;
color: var(--p-inputtext-color);
background: var(--p-inputtext-background);
border: 1px solid var(--p-inputtext-border-color);
}
input.error {
border-color: var(--p-inputtext-invalid-border-color);
}
<\/style>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<FormField v-slot="$field" name="username" initialValue="" class="flex flex-col gap-2">
<input type="text" placeholder="Username" :class="[{ error: $field?.invalid }]" v-bind="$field.props" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="password" initialValue="PrimeVue" class="flex flex-col gap-2">
<input v-model="$field.value" type="password" placeholder="Password" :class="[{ error: $field?.invalid }]" @input="$field.onInput" @blur="$field.onBlur" @change="$field.onChange" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script setup>
import { reactive } from 'vue';
import { zodResolver } from '@primevue/forms/resolvers';
import { z } from 'zod';
import { useToast } from 'primevue/usetoast';
const toast = useToast();
const resolver = zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' }),
password: z.string().min(1, { message: 'Password is required.' })
})
);
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
<style scoped>
input {
width: 100%;
color: var(--p-inputtext-color);
background: var(--p-inputtext-background);
border: 1px solid var(--p-inputtext-border-color);
}
input.error {
border-color: var(--p-inputtext-invalid-border-color);
}
<\/style>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>
<style scoped>
input {
width: 100%;
color: var(--p-inputtext-color);
background: var(--p-inputtext-background);
border: 1px solid var(--p-inputtext-border-color);
}
input.error {
border-color: var(--p-inputtext-invalid-border-color);
}
</style>

View File

@ -0,0 +1,247 @@
<template>
<DocSectionText label="Resolver" :level="2" v-bind="$attrs">
<p>Each FormField can have its own dedicated resolver, allowing you to define custom validation logic for individual fields. This flexibility enables tailored validation rules, ensuring that each form field meets specific criteria.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-80">
<FormField v-slot="$field" name="username" initialValue="" :resolver="zodUserNameResolver" class="flex flex-col gap-2">
<InputText type="text" placeholder="Username" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="firstname" initialValue="" :resolver="yupFirstNameResolver" class="flex flex-col gap-2">
<InputText type="text" placeholder="First Name" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="lastname" initialValue="" :resolver="valibotLastNameResolver" class="flex flex-col gap-2">
<InputText type="text" placeholder="Last Name" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="password" initialValue="" :resolver="customPasswordResolver" class="flex flex-col gap-2">
<Password type="text" placeholder="Password" :feedback="false" toggleMask fluid />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="details" class="flex flex-col gap-2">
<Textarea placeholder="Details" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8', yup: '1.4.0', valibot: '0.42.1' }" />
</template>
<script>
import { valibotResolver, yupResolver, zodResolver } from '@primevue/forms/resolvers';
import * as v from 'valibot';
import * as yup from 'yup';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
details: ''
},
resolver: zodResolver(
z.object({
details: z.string().min(1, { message: 'Details is required via Form Resolver.' })
})
),
zodUserNameResolver: zodResolver(z.string().min(1, { message: 'Username is required via Zod.' })),
yupFirstNameResolver: yupResolver(yup.string().required('First name is required via Yup.')),
valibotLastNameResolver: valibotResolver(v.pipe(v.string(), v.minLength(1, 'Last name is required via Valibot.'))),
code: {
basic: `
<Form :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-80">
<FormField v-slot="$field" name="username" initialValue="" :resolver="zodUserNameResolver" class="flex flex-col gap-2">
<InputText type="text" placeholder="Username" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="firstname" initialValue="" :resolver="yupFirstNameResolver" class="flex flex-col gap-2">
<InputText type="text" placeholder="First Name" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="lastname" initialValue="" :resolver="valibotLastNameResolver" class="flex flex-col gap-2">
<InputText type="text" placeholder="Last Name" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="password" initialValue="" :resolver="customPasswordResolver" class="flex flex-col gap-2">
<Password type="text" placeholder="Password" :feedback="false" toggleMask fluid />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="details" class="flex flex-col gap-2">
<Textarea placeholder="Details" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Toast />
<Form :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-80">
<FormField v-slot="$field" name="username" initialValue="" :resolver="zodUserNameResolver" class="flex flex-col gap-2">
<InputText type="text" placeholder="Username" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="firstname" initialValue="" :resolver="yupFirstNameResolver" class="flex flex-col gap-2">
<InputText type="text" placeholder="First Name" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="lastname" initialValue="" :resolver="valibotLastNameResolver" class="flex flex-col gap-2">
<InputText type="text" placeholder="Last Name" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="password" initialValue="" :resolver="customPasswordResolver" class="flex flex-col gap-2">
<Password type="text" placeholder="Password" :feedback="false" toggleMask fluid />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="details" class="flex flex-col gap-2">
<Textarea placeholder="Details" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { valibotResolver, yupResolver, zodResolver } from '@primevue/forms/resolvers';
import * as v from 'valibot';
import * as yup from 'yup';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
details: ''
},
resolver: zodResolver(
z.object({
details: z.string().min(1, { message: 'Details is required via Form Resolver.' })
})
),
zodUserNameResolver: zodResolver(z.string().min(1, { message: 'Username is required via Zod.' })),
yupFirstNameResolver: yupResolver(yup.string().required('First name is required via Yup.')),
valibotLastNameResolver: valibotResolver(v.pipe(v.string(), v.minLength(1, 'Last name is required via Valibot.')))
};
},
methods: {
customPasswordResolver: ({ value }) => {
const errors = [];
if (!value) {
errors.push({ message: 'Password is required via Custom.' });
}
return {
errors
};
},
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-80">
<FormField v-slot="$field" name="username" initialValue="" :resolver="zodUserNameResolver" class="flex flex-col gap-2">
<InputText type="text" placeholder="Username" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="firstname" initialValue="" :resolver="yupFirstNameResolver" class="flex flex-col gap-2">
<InputText type="text" placeholder="First Name" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="lastname" initialValue="" :resolver="valibotLastNameResolver" class="flex flex-col gap-2">
<InputText type="text" placeholder="Last Name" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="password" initialValue="" :resolver="customPasswordResolver" class="flex flex-col gap-2">
<Password type="text" placeholder="Password" :feedback="false" toggleMask fluid />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" name="details" class="flex flex-col gap-2">
<Textarea placeholder="Details" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script setup>
import { reactive } from 'vue';
import { valibotResolver, yupResolver, zodResolver } from '@primevue/forms/resolvers';
import * as v from 'valibot';
import * as yup from 'yup';
import { z } from 'zod';
import { useToast } from 'primevue/usetoast';
const toast = useToast();
const initialValues = reactive({
details: ''
});
const resolver = zodResolver(
z.object({
details: z.string().min(1, { message: 'Details is required via Form Resolver.' })
})
);
const zodUserNameResolver = zodResolver(z.string().min(1, { message: 'Username is required via Zod.' }));
const yupFirstNameResolver = yupResolver(yup.string().required('First name is required via Yup.'));
const valibotLastNameResolver = valibotResolver(v.pipe(v.string(), v.minLength(1, 'Last name is required via Valibot.')));
const customPasswordResolver = ({ value }) => {
const errors = [];
if (!value) {
errors.push({ message: 'Password is required via Custom.' });
}
return {
errors
};
};
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
customPasswordResolver: ({ value }) => {
const errors = [];
if (!value) {
errors.push({ message: 'Password is required via Custom.' });
}
return {
errors
};
},
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,150 @@
<template>
<DocSectionText label="Template" :level="2" v-bind="$attrs">
<p>It renders as a HTML div element, but this behavior can be modified using the <i>as</i> and <i>asChild</i> props to render different HTML elements or to pass a custom component, allowing for greater flexibility in form structure.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<FormField v-slot="$field" as="section" name="username" initialValue="" class="flex flex-col gap-2">
<InputText type="text" placeholder="Username" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" asChild name="password" initialValue="">
<section class="flex flex-col gap-2">
<Password type="text" placeholder="Password" :feedback="false" toggleMask fluid />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</section>
</FormField>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/forms/resolvers';
import { z } from 'zod';
export default {
data() {
return {
resolver: zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' }),
password: z.string().min(1, { message: 'Password is required.' })
})
),
code: {
basic: `
<Form :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<FormField v-slot="$field" as="section" name="username" initialValue="" class="flex flex-col gap-2">
<InputText type="text" placeholder="Username" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" asChild name="password" initialValue="">
<section class="flex flex-col gap-2">
<Password type="text" placeholder="Password" :feedback="false" toggleMask fluid />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</section>
</FormField>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Toast />
<Form :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<FormField v-slot="$field" as="section" name="username" initialValue="" class="flex flex-col gap-2">
<InputText type="text" placeholder="Username" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" asChild name="password" initialValue="">
<section class="flex flex-col gap-2">
<Password type="text" placeholder="Password" :feedback="false" toggleMask fluid />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</section>
</FormField>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/forms/resolvers';
import { z } from 'zod';
export default {
data() {
return {
resolver: zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' }),
password: z.string().min(1, { message: 'Password is required.' })
})
)
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<FormField v-slot="$field" as="section" name="username" initialValue="" class="flex flex-col gap-2">
<InputText type="text" placeholder="Username" />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</FormField>
<FormField v-slot="$field" asChild name="password" initialValue="">
<section class="flex flex-col gap-2">
<Password type="text" placeholder="Password" :feedback="false" toggleMask fluid />
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
</section>
</FormField>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script setup>
import { reactive } from 'vue';
import { zodResolver } from '@primevue/forms/resolvers';
import { z } from 'zod';
import { useToast } from 'primevue/usetoast';
const toast = useToast();
const resolver = zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' }),
password: z.string().min(1, { message: 'Password is required.' })
})
);
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -69,4 +69,4 @@
"engines": {
"node": ">=12.11.0"
}
}
}

View File

@ -4,7 +4,7 @@
header="Forms"
description="The PrimeVue Forms library provides comprehensive form state management with built-in validation support."
:componentDocs="docs"
:apiDocs="['Form']"
:apiDocs="['Form', 'FormField']"
:ptTabComponent="ptComponent"
:themingDocs="themingDoc"
/>
@ -12,11 +12,11 @@
<script>
import AccessibilityDoc from '@/doc/forms/AccessibilityDoc.vue';
import DownloadDoc from '@/doc/forms/DownloadDoc.vue';
import BasicDoc from '@/doc/forms/BasicDoc.vue';
import DownloadDoc from '@/doc/forms/DownloadDoc.vue';
import DynamicDoc from '@/doc/forms/DynamicDoc.vue';
import FormFieldDoc from '@/doc/forms/FormFieldDoc.vue';
import ImportDoc from '@/doc/forms/ImportDoc.vue';
import RegisterDoc from '@/doc/forms/RegisterDoc.vue';
import ResolversDoc from '@/doc/forms/ResolversDoc.vue';
import StatesDoc from '@/doc/forms/StatesDoc.vue';
import SubmitDoc from '@/doc/forms/SubmitDoc.vue';
@ -59,9 +59,9 @@ export default {
component: ValidateOnDoc
},
{
id: 'register',
label: 'Register',
component: RegisterDoc
id: 'formfield',
label: 'FormField',
component: FormFieldDoc
},
{
id: 'submit',