Skip to content

Commit

Permalink
feat: added values helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
logaretm committed Dec 18, 2020
1 parent 8f7d8e8 commit e0f16d6
Show file tree
Hide file tree
Showing 7 changed files with 260 additions and 5 deletions.
51 changes: 46 additions & 5 deletions docs/content/api/composition-helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ import { useIsFormDirty } from 'vee-validate';

const isDirty = useIsFormDirty();

isDirty.value; // if form exists: true or false
isDirty.value; // true or false
```

<code-title level="4">
Expand All @@ -114,7 +114,7 @@ import { useIsFieldTouched } from 'vee-validate';

const isTouched = useIsFieldTouched('fieldName');

isTouched.value; // if form exists: true or false
isTouched.value; // true or false
```

You can also use it in a child component that has a parent that used `useField`, The `useIsFieldTouched` will automatically pick up the field and produce its meta `touched` value
Expand All @@ -139,7 +139,7 @@ import { useIsFormTouched } from 'vee-validate';

const isTouched = useIsFormTouched();

isTouched.value; // if form exists: true or false
isTouched.value; // true or false
```

<code-title level="4">
Expand All @@ -155,7 +155,7 @@ import { useIsFieldValid } from 'vee-validate';

const isValid = useIsFieldValid('fieldName');

isValid.value; // if form exists: true or false
isValid.value; // true or false
```

You can also use it in a child component that has a parent that used `useField`, The `useIsFieldValid` will automatically pick up the field and produce its meta `valid` value
Expand Down Expand Up @@ -186,7 +186,7 @@ import { useIsFormValid } from 'vee-validate';

const isValid = useIsFormValid();

isValid.value; // if form exists: true or false
isValid.value; // true or false
```

<doc-tip type="warn">
Expand Down Expand Up @@ -283,3 +283,44 @@ const resetForm = useResetForm();

resetForm(); // resets the form
```

<code-title level="4">

`useFieldValue(field?: string): ComputedRef<any>`

</code-title>

Returns a computed ref to the specified field's current value.

```js
import { useFieldValue } from 'vee-validate';

const currentValue = useFieldValue('fieldName');

currentValue.value;
```

You can also use it in a child component that has a parent that used `useField`, The `useFieldValue` will automatically pick up the field and produce its current value.

```js
import { useFieldValue } from 'vee-validate';

// Will look for the first parent that used `useField`
const currentValue = useFieldValue();
```

<code-title level="4">

`useFormValues(): ComputedRef<Record<string, any>>`

</code-title>

Returns a computed ref to the context form current values.

```js
import { useFormValues } from 'vee-validate';

const values = useFormValues();

values.value;
```
2 changes: 2 additions & 0 deletions docs/content/guide/composition-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,5 +192,7 @@ Here is a list of the functions available that you can use:
- `useResetForm` Resets the form to its initial state
- `useIsSubmitting` If the form is currently submitting
- `useSubmitCount` The number of times the user attempted to submit the form
- `useFieldValue` Returns a specific fields' current value
- `useFormValues` Returns the current form field values

For more information about the functions, you can head over to the [API reference and check them out](/api/composition-helpers).
2 changes: 2 additions & 0 deletions packages/vee-validate/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ export { useIsFormTouched } from './useIsFormTouched';
export { useIsFormValid } from './useIsFormValid';
export { useValidateForm } from './useValidateForm';
export { useSubmitCount } from './useSubmitCount';
export { useFieldValue } from './useFieldValue';
export { useFormValues } from './useFormValues';
21 changes: 21 additions & 0 deletions packages/vee-validate/src/useFieldValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { computed, inject, unref } from 'vue';
import { FieldContext, FormSymbol } from './symbols';
import { MaybeReactive } from './types';
import { getFromPath, injectWithSelf } from './utils';

/**
* Gives access to a field's current value
*/
export function useFieldValue<TValue = any>(path?: MaybeReactive<string>) {
const form = injectWithSelf(FormSymbol);
// We don't want to use self injected context as it doesn't make sense
const field = path ? undefined : inject(FieldContext);

return computed(() => {
if (path) {
return getFromPath(form?.values, unref(path)) as TValue | undefined;
}

return field?.value?.value as TValue | undefined;
});
}
18 changes: 18 additions & 0 deletions packages/vee-validate/src/useFormValues.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { computed } from 'vue';
import { FormSymbol } from './symbols';
import { FormContext } from './types';
import { injectWithSelf, warn } from './utils';

/**
* Gives access to a form's values
*/
export function useFormValues<TValues extends Record<string, any> = Record<string, any>>() {
const form = injectWithSelf(FormSymbol) as FormContext<TValues> | undefined;
if (!form) {
warn('No vee-validate <Form /> or `useForm` was detected in the component tree');
}

return computed(() => {
return form?.values || ({} as Partial<TValues>);
});
}
113 changes: 113 additions & 0 deletions packages/vee-validate/tests/useFieldValue.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import flushPromises from 'flush-promises';
import { useField, useFieldValue, useForm } from '@/vee-validate';
import { mountWithHoc, setValue } from './helpers';
import { defineComponent } from 'vue';

describe('useFieldValue()', () => {
const REQUIRED_MESSAGE = 'Field is required';
const validate = (val: any) => (val ? true : REQUIRED_MESSAGE);

test('gives access to a single field value', async () => {
mountWithHoc({
setup() {
useForm();
const { value } = useField('test', validate);
const currValue = useFieldValue('test');

return {
value,
currValue,
};
},
template: `
<input name="field" v-model="value" />
<span>{{ currValue }}</span>
`,
});

await flushPromises();
const input = document.querySelector('input');
const valueSpan = document.querySelector('span');
const inputValue = '1234';
setValue(input as any, inputValue);
await flushPromises();
expect(valueSpan?.textContent).toBe(inputValue);
});

test('gives access to a single field value in a child component with specifying a path', async () => {
const CustomErrorComponent = defineComponent({
template: '<span>{{ value }}</span>',
setup() {
const value = useFieldValue();

return {
value,
};
},
});
mountWithHoc({
components: {
CustomErrorComponent,
},
setup() {
useForm();
const { value } = useField('test', validate);

return {
value,
};
},
template: `
<input name="field" v-model="value" />
<CustomErrorComponent />
`,
});

await flushPromises();
const input = document.querySelector('input');
const valueSpan = document.querySelector('span');
const inputValue = '1234';
setValue(input as any, inputValue);
await flushPromises();
expect(valueSpan?.textContent).toBe(inputValue);
});

test('returns undefined if field not found', async () => {
mountWithHoc({
setup() {
useForm();
const value = useFieldValue('something');

return {
value,
};
},
template: `
<span>{{ value }}</span>
`,
});

await flushPromises();
const error = document.querySelector('span');
expect(error?.textContent).toBe('');
});

test('returns undefined if form is not found', async () => {
mountWithHoc({
setup() {
const value = useFieldValue('something');

return {
value,
};
},
template: `
<span>{{ value }}</span>
`,
});

await flushPromises();
const error = document.querySelector('span');
expect(error?.textContent).toBe('');
});
});
58 changes: 58 additions & 0 deletions packages/vee-validate/tests/useFormValues.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import flushPromises from 'flush-promises';
import { useField, useForm, useFormValues } from '@/vee-validate';
import { mountWithHoc, setValue } from './helpers';

describe('useFormValues()', () => {
const REQUIRED_MESSAGE = 'Field is required';
const validate = (val: any) => (val ? true : REQUIRED_MESSAGE);

test('gives access to all form values', async () => {
mountWithHoc({
setup() {
useForm();
const { value } = useField('test', validate);
const values = useFormValues();

return {
value,
values,
};
},
template: `
<input name="field" v-model="value" />
<span>{{ values.test }}</span>
`,
});

await flushPromises();
const input = document.querySelector('input');
const valueSpan = document.querySelector('span');
const inputValue = '1234';
setValue(input as any, inputValue);
await flushPromises();
expect(valueSpan?.textContent).toBe(inputValue);
});

test('returns empty object and warns if form is not found', async () => {
const spy = jest.spyOn(console, 'warn').mockImplementation();

mountWithHoc({
setup() {
const values = useFormValues();

return {
values,
};
},
template: `
<span>{{ values }}</span>
`,
});

await flushPromises();
const valuesSpan = document.querySelector('span');
expect(valuesSpan?.textContent).toBe('{}');
expect(spy).toHaveBeenCalled();
spy.mockRestore();
});
});

0 comments on commit e0f16d6

Please sign in to comment.