From 4574581a09127dadd6c93dc40fb2e04213ea68a7 Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Wed, 7 Jul 2021 00:43:11 +0200 Subject: [PATCH] feat: added standalone prop for fields --- docs/content/api/field.md | 1 + docs/content/api/use-field.md | 1 + packages/vee-validate/src/Field.ts | 6 +++++ packages/vee-validate/src/useField.ts | 23 +++++++++++++--- packages/vee-validate/tests/Form.spec.ts | 34 ++++++++++++++++++++++++ 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/docs/content/api/field.md b/docs/content/api/field.md index 830aa6759..0d2149951 100644 --- a/docs/content/api/field.md +++ b/docs/content/api/field.md @@ -138,6 +138,7 @@ Note that you no longer should use `v-model` on your input as `v-bind="field"` w | value | `any` | `undefined` | The field's initial value, optional as long as the field type is not `checkbox` or `radio`. | | type | `string` | `undefined` | The field type, must be provided if you want your field to behave as a `checkbox` or a `radio` input. | | unchecked-value | `any` | `undefined` | Only useful when the `type="checkbox"` and the field is a single checkbox field (not bound to an array value). Controls the input's value when it's unchecked. | +| standalone | `boolean` | `false` | Excludes the field from participating in any `Form` or `useForm` contexts, useful for creating inputs that do contribute to the `values` object | ### Slots diff --git a/docs/content/api/use-field.md b/docs/content/api/use-field.md index db5c94bb0..bfc5832f4 100644 --- a/docs/content/api/use-field.md +++ b/docs/content/api/use-field.md @@ -90,6 +90,7 @@ interface FieldOptions { type?: string; // The input type, can be any string. Toggles specific toggle mode for `checkbox` checkedValue?: string; // Used the input type is `checkbox` or `radio` otherwise ignored uncheckedValue?: string; // Used the input type is `checkbox` otherwise ignored + standalone?: boolean; // Excludes the field from participating in any `Form` or `useForm` contexts, useful for creating inputs that do contribute to the `values` } interface ValidationResult { diff --git a/packages/vee-validate/src/Field.ts b/packages/vee-validate/src/Field.ts index 0332ae43b..add84a002 100644 --- a/packages/vee-validate/src/Field.ts +++ b/packages/vee-validate/src/Field.ts @@ -53,6 +53,7 @@ export const Field = defineComponent({ type: Boolean, default: () => getConfig().bails, }, + label: { type: String, default: undefined, @@ -73,6 +74,10 @@ export const Field = defineComponent({ type: null as unknown as PropType<((e: any) => unknown) | undefined>, default: undefined, }, + standalone: { + type: Boolean, + default: false, + }, }, setup(props, ctx) { const rules = toRef(props, 'rules'); @@ -98,6 +103,7 @@ export const Field = defineComponent({ } = useField(name, rules, { validateOnMount: props.validateOnMount, bails: props.bails, + standalone: props.standalone, type: ctx.attrs.type as string, initialValue: resolveInitialValue(props, ctx), // Only for checkboxes and radio buttons diff --git a/packages/vee-validate/src/useField.ts b/packages/vee-validate/src/useField.ts index 986a61b78..87d423f75 100644 --- a/packages/vee-validate/src/useField.ts +++ b/packages/vee-validate/src/useField.ts @@ -50,6 +50,7 @@ interface FieldOptions { checkedValue?: MaybeRef; uncheckedValue?: MaybeRef; label?: MaybeRef; + standalone?: boolean; } type RuleExpression = @@ -71,10 +72,20 @@ export function useField( opts?: Partial> ): FieldComposable { const fid = ID_COUNTER >= Number.MAX_SAFE_INTEGER ? 0 : ++ID_COUNTER; - const { initialValue, validateOnMount, bails, type, checkedValue, label, validateOnValueUpdate, uncheckedValue } = - normalizeOptions(unref(name), opts); + const { + initialValue, + validateOnMount, + bails, + type, + checkedValue, + label, + validateOnValueUpdate, + uncheckedValue, + standalone, + } = normalizeOptions(unref(name), opts); + + const form = !standalone ? injectWithSelf(FormContextSymbol) : undefined; - const form = injectWithSelf(FormContextSymbol); const { meta, errors, @@ -92,6 +103,7 @@ export function useField( form, type, checkedValue, + standalone, }); const normalizedRules = computed(() => { @@ -293,6 +305,7 @@ function normalizeOptions(name: string, opts: Partial({ form, type, checkedValue, + standalone, }: { name: MaybeRef; checkedValue?: MaybeRef; initValue?: MaybeRef; form?: FormContext; type?: string; + standalone?: boolean; }) { const { errors, errorMessage, setErrors } = useFieldErrors(name, form); - const formInitialValues = injectWithSelf(FormInitialValuesSymbol, undefined); + const formInitialValues = standalone ? undefined : injectWithSelf(FormInitialValuesSymbol, undefined); // clones the ref value to a mutable version const initialValueRef = ref(unref(initValue)) as Ref; diff --git a/packages/vee-validate/tests/Form.spec.ts b/packages/vee-validate/tests/Form.spec.ts index 1b6b6715d..c0ca59555 100644 --- a/packages/vee-validate/tests/Form.spec.ts +++ b/packages/vee-validate/tests/Form.spec.ts @@ -2115,4 +2115,38 @@ describe('
', () => { // field was re-checked expect(span.textContent).toBe(''); }); + + test('standalone fields are excluded from form state', async () => { + const wrapper = mountWithHoc({ + setup() { + return {}; + }, + template: ` + + + + {{ errorMessage }} + + {{ errors.fname }} + {{ meta.valid }} + + `, + }); + + await flushPromises(); + const formError = wrapper.$el.querySelector('#formError'); + const fieldError = wrapper.$el.querySelector('#fieldError'); + const meta = wrapper.$el.querySelector('#meta'); + + expect(formError.textContent).toBe(''); + expect(fieldError.textContent).toBe(''); + expect(meta.textContent).toBe('true'); + + setValue(wrapper.$el.querySelector('input'), ''); + await flushPromises(); + + expect(formError.textContent).toBe(''); + expect(fieldError.textContent).toBe(REQUIRED_MESSAGE); + expect(meta.textContent).toBe('true'); + }); });