Skip to content

Commit 40b0fb8

Browse files
committed
feat: support dynamic form field slots
1 parent 70ca761 commit 40b0fb8

File tree

4 files changed

+63
-9
lines changed

4 files changed

+63
-9
lines changed

docs/app/components/examples/Slots.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,13 @@ const schema = z.object({
2323
]"
2424
/>
2525
</template>
26+
27+
<template #boolean-description>
28+
<span class="text-green-500">Green description!</span>
29+
</template>
30+
31+
<template #boolean-hint>
32+
<span class="text-purple-500">Purple hint</span>
33+
</template>
2634
</AutoForm>
2735
</template>

docs/content/2.customization/3.slots.md

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ description: Configure specific fields of the form
66
Sometimes you may want to customize the rendering of specific fields in your form.
77
If all other methods fail you can use slots to achieve this.
88

9-
| Slot | Type | Description |
10-
|-----------------|---------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
11-
| `before-fields` | | Slot inside [`<UForm>`](https://ui.nuxt.com/components/form) before the list of [`<UFormField>`](https://ui.nuxt.com/components/form-field) |
12-
| `after-fields` | | Slot inside [`<UForm>`](https://ui.nuxt.com/components/form) after the list of [`<UFormField>`](https://ui.nuxt.com/components/form-field) |
13-
| `submit` | `{ disabled: boolean }` | Slot for customizing the submit button. `disabled` indicates whether at least one field is invalid. |
14-
| `<field>` | `{ field: string, state: Ref<Record<string, any> }>`{lang=ts} | **Dynamic** slot for a specific field inside Zod schema. |
9+
| Slot | Type | Description |
10+
|-----------------------------|---------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
11+
| `before-fields` | | Slot inside [`<UForm>`](https://ui.nuxt.com/components/form) before the list of [`<UFormField>`](https://ui.nuxt.com/components/form-field) |
12+
| `after-fields` | | Slot inside [`<UForm>`](https://ui.nuxt.com/components/form) after the list of [`<UFormField>`](https://ui.nuxt.com/components/form-field) |
13+
| `submit` | `{ disabled: boolean }` | Slot for customizing the submit button. `disabled` indicates whether at least one field is invalid. |
14+
| `<field>` | `{ field: string, state: Ref<Record<string, any> }>`{lang=ts} | **Dynamic** input slot for a specific field inside Zod schema. |
15+
| `<field>-<form field slot>` | | **Dynamic** sub-slot for a specific `<UFormField>` inside the form. |
1516

16-
### Dynamic slots
17+
### Dynamic input slots
1718

1819
For each field in your Zod schema, `<AutoForm>` generates a dynamic slot.
1920
You can use these slots to customize the input components for each field.
@@ -52,11 +53,38 @@ const schema = z.object({
5253

5354
And if you want to use a custom component for the submit button, you can do it like this:
5455

56+
```vue [MyForm.vue]{3-17}
57+
<template>
58+
<AutoForm :schema="schema">
59+
<template #boolean="{ field, state: stateValue }">
60+
<USelect
61+
v-model="stateValue[field]"
62+
:items="[
63+
{
64+
label: 'False Value',
65+
value: false,
66+
},
67+
{
68+
label: 'True Value',
69+
value: true,
70+
},
71+
]"
72+
/>
73+
</template>
74+
</AutoForm>
75+
</template>
76+
```
77+
78+
## Dynamic `UFormField` slots
79+
80+
Beyond dynamic input slots, you can override additional field elements such as `hint` or `description`.
81+
When the standard [FormField](/customization/fields#formfield-customization) configuration does not provide enough flexibility, use slots to customize these elements.
82+
5583
```vue [MyForm.vue]{3-5}
5684
<template>
5785
<AutoForm :schema="schema">
58-
<template #submit="{ disabled }">
59-
<SubmitButton :disabled />
86+
<template #text-hint>
87+
This is a hint for the text field.
6088
</template>
6189
</AutoForm>
6290
</template>

playground/app/components/MyForm.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ function onSubmit(data: Record<string, any>) {
6363
:schema="schema"
6464
@submit="onSubmit"
6565
>
66+
<template #text-hint>
67+
<NuxtLink to="https://norbiros.dev">
68+
Who?
69+
</NuxtLink>
70+
</template>
6671
<template #custom_bool="{ field, state }">
6772
<USelect
6873
v-model="state[field]"

src/runtime/components/AutoForm.vue

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ const emit = defineEmits<{
2424
(e: 'submit', data: InferOutput<T>): void
2525
}>()
2626
27+
const slots = useSlots()
28+
2729
const state = reactive({ ...props.initialState })
2830
2931
defineExpose({ submit })
@@ -53,13 +55,20 @@ const fields = Object.entries(shape).map(([key, zodType]: [string, any]) => {
5355
key,
5456
formField: {
5557
name: key,
58+
slots: findSlots(key),
5659
...parseMeta(meta, key),
5760
},
5861
component: meta?.input?.component ?? result.component,
5962
props: defu(meta?.input?.props, result.componentProps ?? {}),
6063
}
6164
}).filter((field): field is NonNullable<typeof field> => field != null)
6265
66+
function findSlots(key: string): string[] {
67+
return Object.keys(slots)
68+
.filter(name => name.startsWith(`${key}-`))
69+
.map(name => name.slice(key.length + 1))
70+
}
71+
6372
function parseMeta(meta: any, key: string) {
6473
return {
6574
label: meta.title ?? upperFirst(splitByCase(key).join(' ').toLowerCase()),
@@ -114,6 +123,10 @@ const submitButtonProps = computed(() => {
114123
:ui="{ description: 'text-left' }"
115124
v-bind="field.formField"
116125
>
126+
<template v-for="slot in field.formField.slots" #[slot]>
127+
<slot :name="`${field.key}-${slot}`" />
128+
</template>
129+
117130
<slot
118131
:name="field.key"
119132
:field="field.key"

0 commit comments

Comments
 (0)