Skip to content

Commit 78c4668

Browse files
committed
feat: allow null as a valid Form prop type closes #4483
1 parent a0d34ce commit 78c4668

File tree

4 files changed

+46
-8
lines changed

4 files changed

+46
-8
lines changed

.changeset/green-wolves-reply.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'vee-validate': patch
3+
---
4+
5+
feat: allow null as a valid Form prop type closes #4483

packages/vee-validate/src/Form.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const FormImpl = /** #__PURE__ */ defineComponent({
4040
inheritAttrs: false,
4141
props: {
4242
as: {
43-
type: String,
43+
type: null as unknown as PropType<string | null>,
4444
default: 'form',
4545
},
4646
validationSchema: {
@@ -196,16 +196,16 @@ const FormImpl = /** #__PURE__ */ defineComponent({
196196

197197
return function renderForm() {
198198
// avoid resolving the form component as itself
199-
const tag = props.as === 'form' ? props.as : (resolveDynamicComponent(props.as) as string);
199+
const tag = props.as === 'form' ? props.as : !props.as ? null : (resolveDynamicComponent(props.as) as string);
200200
const children = normalizeChildren(tag, ctx, slotProps as any);
201201

202-
if (!props.as) {
202+
if (!tag) {
203203
return children;
204204
}
205205

206206
// Attributes to add on a native `form` tag
207207
const formAttrs =
208-
props.as === 'form'
208+
tag === 'form'
209209
? {
210210
// Disables native validation as vee-validate will handle it.
211211
novalidate: true,

packages/vee-validate/src/utils/vnode.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { SetupContext } from 'vue';
33
type HTMLElementWithValueBinding = HTMLElement & { _value: unknown };
44

55
// eslint-disable-next-line @typescript-eslint/no-explicit-any
6-
export const normalizeChildren = (
7-
tag: string | Record<string, unknown> | undefined,
6+
export function normalizeChildren(
7+
tag: string | null,
88
context: SetupContext<any>,
99
slotProps: () => Record<string, unknown>,
10-
) => {
10+
) {
1111
if (!context.slots.default) {
1212
return context.slots.default;
1313
}
@@ -19,7 +19,7 @@ export const normalizeChildren = (
1919
return {
2020
default: () => context.slots.default?.(slotProps()),
2121
};
22-
};
22+
}
2323

2424
/**
2525
* Vue adds a `_value` prop at the moment on the input elements to store the REAL value on them, real values are different than the `value` attribute

packages/vee-validate/tests/Form.spec.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,39 @@ describe('<Form />', () => {
351351
expect(submitMock).toHaveBeenCalledTimes(1);
352352
});
353353

354+
test('can be renderless with null', async () => {
355+
const submitMock = vi.fn();
356+
const wrapper = mountWithHoc({
357+
template: `
358+
<div>
359+
<VForm :as="null" v-slot="{ errors, submitForm }">
360+
<form @submit="submitForm">
361+
<Field name="field" rules="required" />
362+
<span id="error">{{ errors.field }}</span>
363+
364+
<button>Validate</button>
365+
</form>
366+
</VForm>
367+
</div>
368+
`,
369+
});
370+
371+
const form = wrapper.$el.querySelector('form');
372+
form.submit = submitMock;
373+
const input = wrapper.$el.querySelector('input');
374+
await flushPromises();
375+
376+
wrapper.$el.querySelector('button').click();
377+
await flushPromises();
378+
expect(submitMock).toHaveBeenCalledTimes(0);
379+
380+
setValue(input, '12');
381+
wrapper.$el.querySelector('button').click();
382+
await flushPromises();
383+
384+
expect(submitMock).toHaveBeenCalledTimes(1);
385+
});
386+
354387
test('validation schema with yup', async () => {
355388
const wrapper = mountWithHoc({
356389
setup() {

0 commit comments

Comments
 (0)