Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/content/api/form.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ While not recommended, you can make the `Form` component a renderless component
| validationSchema | `Record<string, string \| Function>` | `undefined` | An object describing a schema to validate fields with, can be a plain object or a `yup` object schema |
| initialValues | `Record<string, any>` | `undefined` | Initial values to fill the fields with, when provided the fields will be validated on mounted |
| initialErrors | `Record<string, string>` | `undefined` | Initial form errors to fill the fields with, the errors will be added when the form component is mounted |
| initialDirty | `Record<string, boolean>` | `undefined` | Initial dirty fields, the status will be applied when the form component is mounted |
| initialTouched | `Record<string, boolean>` | `undefined` | Initial touched fields, the status will be applied when the form component is mounted |
| validateOnMount | `boolean` | `false` | If true, the fields currently present in the form will be validated when the `<Form />` component is mounted |

### Slots
Expand Down
50 changes: 50 additions & 0 deletions docs/content/api/use-form.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ interface FormOptions {
validationSchema?: any; // A yup schema, or a Record<string, any> containing valid rules as `useField`
initialValues?: Record<string, any>;
initialErrors?: Record<string, string>; // a map of the form's initial error messages
initialDirty?: Record<string, boolean>; // a map of the form's initial dirty fields
initialTouched?: Record<string, boolean>; // a map of the form's initial touched fields
validateOnMount?: boolean;
}

Expand Down Expand Up @@ -70,6 +72,54 @@ Enables form-level validation, uses the specified schema to validate the fields.

The initial values for the form, can be a reactive object or reference.

```js
const { ... } = useForm({
initialErrors: {
email: 'example@gmail.com',
password: 'p@$$w0rd',
}
});
```

#### `initialErrors?: Record<string, string>`

The initial errors for the fields, useful for non hydrated SSR applications like Laravel, errors are applied on mounted.

```js
const { ... } = useForm({
initialErrors: {
email: 'This email is invalid',
password: 'Password too short',
}
});
```

#### `initialDirty?: Record<string, any>`

The initial dirty status for the form fields, applied on mounted.

```js
const { ... } = useForm({
initialDirty: {
email: true, // dirty
password: false, // non-dirty
}
});
```

#### `initialTouched?: Record<string, any>`

The initial touched status for the form fields, applied on mounted.

```js
const { ... } = useForm({
initialTouched: {
email: true, // touched
password: false, // non-touched
}
});
```

#### `validateOnMount?: boolean`

If true, it will trigger validation for all fields once the form component is mounted.
Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/Form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ export const Form = defineComponent({
type: Object,
default: undefined,
},
initialDirty: {
type: Object,
default: undefined,
},
initialTouched: {
type: Object,
default: undefined,
},
validateOnMount: {
type: Boolean,
default: false,
Expand Down Expand Up @@ -51,6 +59,8 @@ export const Form = defineComponent({
validationSchema: props.validationSchema,
initialValues,
initialErrors: props.initialErrors,
initialTouched: props.initialTouched,
initialDirty: props.initialDirty,
validateOnMount: props.validateOnMount,
});

Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/useForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ interface FormOptions<TValues extends Record<string, any>> {
| ObjectSchema<TValues>;
initialValues?: MaybeReactive<TValues>;
initialErrors?: Record<keyof TValues, string | undefined>;
initialTouched?: Record<keyof TValues, boolean>;
initialDirty?: Record<keyof TValues, boolean>;
validateOnMount?: boolean;
}

Expand Down Expand Up @@ -332,6 +334,14 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
setErrors(opts.initialErrors);
}

if (opts?.initialDirty) {
setDirty(opts.initialDirty);
}

if (opts?.initialTouched) {
setTouched(opts.initialTouched);
}

if (opts?.validateOnMount) {
validate();
}
Expand Down
52 changes: 52 additions & 0 deletions packages/core/tests/Form.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1381,4 +1381,56 @@ describe('<Form />', () => {
expect(errorEls[0].textContent).toBe(errors.email);
expect(errorEls[1].textContent).toBe(errors.password);
});

test('sets initial dirty with initialDirty', async () => {
const dirty = {
email: true,
};
const wrapper = mountWithHoc({
setup() {
return {
dirty,
};
},
template: `
<VForm ref="form" :initial-dirty="dirty">
<Field id="email" name="email" v-slot="{ meta, field }">
<input v-bind="field" />
<span>{{ meta.dirty }}</span>
</Field>
</VForm>
`,
});

await flushPromises();
const meta = wrapper.$el.querySelector('span');
await flushPromises();
expect(meta.textContent).toBe('true');
});

test('sets touched dirty with touchedDirty', async () => {
const touched = {
email: true,
};
const wrapper = mountWithHoc({
setup() {
return {
touched,
};
},
template: `
<VForm ref="form" :initial-touched="touched">
<Field id="email" name="email" v-slot="{ meta, field }">
<input v-bind="field" />
<span>{{ meta.touched }}</span>
</Field>
</VForm>
`,
});

await flushPromises();
const meta = wrapper.$el.querySelector('span');
await flushPromises();
expect(meta.textContent).toBe('true');
});
});