diff --git a/packages/vee-validate/src/types.ts b/packages/vee-validate/src/types.ts index b65a4294d..26c4f97bf 100644 --- a/packages/vee-validate/src/types.ts +++ b/packages/vee-validate/src/types.ts @@ -61,6 +61,7 @@ export interface PrivateFieldComposite { setValidationState(state: ValidationResult): void; setTouched(isTouched: boolean): void; setErrors(message: string | string[]): void; + setValue(value: TValue): void; } export type FieldComposable = Omit, 'idx' | 'fid'>; diff --git a/packages/vee-validate/src/useField.ts b/packages/vee-validate/src/useField.ts index ead79c3b5..f35cda140 100644 --- a/packages/vee-validate/src/useField.ts +++ b/packages/vee-validate/src/useField.ts @@ -184,6 +184,10 @@ export function useField( watchValue(); } + function setValue(newValue: TValue) { + value.value = newValue; + } + const field: PrivateFieldComposite = { idx: -1, fid, @@ -207,6 +211,7 @@ export function useField( setValidationState, setTouched, setErrors, + setValue, }; provide(FieldContextSymbol, field); diff --git a/packages/vee-validate/src/useFieldValue.ts b/packages/vee-validate/src/useFieldValue.ts index cb8648558..85aa9662e 100644 --- a/packages/vee-validate/src/useFieldValue.ts +++ b/packages/vee-validate/src/useFieldValue.ts @@ -11,11 +11,25 @@ export function useFieldValue(path?: MaybeRef) { // We don't want to use self injected context as it doesn't make sense const field = path ? undefined : inject(FieldContextSymbol); - return computed(() => { + const value = computed(() => { if (path) { - return getFromPath(form?.values, unref(path)) as TValue | undefined; + return getFromPath(form?.values, unref(path)) as TValue; } - return field?.value?.value as TValue | undefined; + return field?.value?.value as TValue; }); + + function setValue(newValue: TValue) { + if (path) { + form?.setFieldValue(unref(path), newValue); + return; + } + + field?.setValue(newValue); + } + + return { + value, + setValue, + }; } diff --git a/packages/vee-validate/src/useFormValues.ts b/packages/vee-validate/src/useFormValues.ts index b705c4068..0f1e768af 100644 --- a/packages/vee-validate/src/useFormValues.ts +++ b/packages/vee-validate/src/useFormValues.ts @@ -12,7 +12,21 @@ export function useFormValues = Record or `useForm` was detected in the component tree'); } - return computed(() => { + const values = computed(() => { return form?.values || ({} as Partial); }); + + function setFieldValue(path: string, value: any) { + form?.setFieldValue(path, value); + } + + function setValues(values: Partial) { + form?.setValues(values as any); + } + + return { + values, + setFieldValue, + setValues, + }; } diff --git a/packages/vee-validate/tests/useFieldValue.spec.ts b/packages/vee-validate/tests/useFieldValue.spec.ts index e78cb64e6..8c5967c94 100644 --- a/packages/vee-validate/tests/useFieldValue.spec.ts +++ b/packages/vee-validate/tests/useFieldValue.spec.ts @@ -12,16 +12,18 @@ describe('useFieldValue()', () => { setup() { useForm(); const { value } = useField('test', validate); - const currValue = useFieldValue('test'); + const { value: currValue, setValue } = useFieldValue('test'); return { value, currValue, + setValue, }; }, template: ` {{ currValue }} + `, }); @@ -32,13 +34,19 @@ describe('useFieldValue()', () => { setValue(input as any, inputValue); await flushPromises(); expect(valueSpan?.textContent).toBe(inputValue); + + // test value setting + const btn = document.querySelector('button'); + btn?.click(); + await flushPromises(); + expect(input?.value).toBe('5'); }); - test('gives access to a single field value in a child component with specifying a path', async () => { + test('gives access to a single field value in a child component without specifying a path', async () => { const CustomErrorComponent = defineComponent({ template: '{{ value }}', setup() { - const value = useFieldValue(); + const { value } = useFieldValue(); return { value, @@ -76,7 +84,7 @@ describe('useFieldValue()', () => { mountWithHoc({ setup() { useForm(); - const value = useFieldValue('something'); + const { value } = useFieldValue('something'); return { value, @@ -95,7 +103,7 @@ describe('useFieldValue()', () => { test('returns undefined if form is not found', async () => { mountWithHoc({ setup() { - const value = useFieldValue('something'); + const { value } = useFieldValue('something'); return { value, diff --git a/packages/vee-validate/tests/useFormValues.spec.ts b/packages/vee-validate/tests/useFormValues.spec.ts index 95eca01f0..e9f958aa3 100644 --- a/packages/vee-validate/tests/useFormValues.spec.ts +++ b/packages/vee-validate/tests/useFormValues.spec.ts @@ -11,7 +11,7 @@ describe('useFormValues()', () => { setup() { useForm(); const { value } = useField('test', validate); - const values = useFormValues(); + const { values } = useFormValues(); return { value, @@ -33,12 +33,72 @@ describe('useFormValues()', () => { expect(valueSpan?.textContent).toBe(inputValue); }); + test('can set a single field value', async () => { + mountWithHoc({ + setup() { + useForm(); + const { value } = useField('test', validate); + const { values, setFieldValue } = useFormValues(); + + return { + value, + values, + setFieldValue, + }; + }, + template: ` + + {{ values.test }} + + `, + }); + + await flushPromises(); + const btn = document.querySelector('button'); + const input = document.querySelector('input'); + const valueSpan = document.querySelector('span'); + btn?.click(); + await flushPromises(); + expect(valueSpan?.textContent).toBe('5'); + expect(input?.value).toBe('5'); + }); + + test('can set multiple fields values', async () => { + mountWithHoc({ + setup() { + useForm(); + const { value } = useField('test', validate); + const { values, setValues } = useFormValues(); + + return { + value, + values, + setValues, + }; + }, + template: ` + + {{ values.test }} + + `, + }); + + await flushPromises(); + const btn = document.querySelector('button'); + const input = document.querySelector('input'); + const valueSpan = document.querySelector('span'); + btn?.click(); + await flushPromises(); + expect(valueSpan?.textContent).toBe('5'); + expect(input?.value).toBe('5'); + }); + test('returns empty object and warns if form is not found', async () => { const spy = jest.spyOn(console, 'warn').mockImplementation(); mountWithHoc({ setup() { - const values = useFormValues(); + const { values } = useFormValues(); return { values,