From 3824103d5726787eeab07e8db773e583f8d821ce Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Sat, 31 Jul 2021 15:03:11 +0200 Subject: [PATCH] FIX: avoid setting the absent value to checkboxes without unchecked value closes #3424 --- packages/vee-validate/src/Field.ts | 9 ++++-- packages/vee-validate/src/symbols.ts | 2 +- packages/vee-validate/src/utils/assertions.ts | 4 +-- packages/vee-validate/tests/Form.spec.ts | 28 +++++++++++++++++++ 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/packages/vee-validate/src/Field.ts b/packages/vee-validate/src/Field.ts index add84a002..d07f9c04d 100644 --- a/packages/vee-validate/src/Field.ts +++ b/packages/vee-validate/src/Field.ts @@ -3,7 +3,7 @@ import { getConfig } from './config'; import { useField } from './useField'; import { normalizeChildren, hasCheckedAttr, shouldHaveValueBinding, isPropPresent } from './utils'; import { toNumber } from '../../shared'; -import { EMPTY_VALUE } from './symbols'; +import { IS_ABSENT } from './symbols'; interface ValidationTriggersProps { validateOnMount: boolean; @@ -64,7 +64,7 @@ export const Field = defineComponent({ }, modelValue: { type: null, - default: EMPTY_VALUE, + default: IS_ABSENT, }, modelModifiers: { type: null, @@ -162,6 +162,11 @@ export const Field = defineComponent({ const modelValue = toRef(props, 'modelValue'); watch(modelValue, newModelValue => { + // Don't attempt to sync absent values + if ((newModelValue as any) === IS_ABSENT && value.value === undefined) { + return; + } + if (newModelValue !== applyModifiers(value.value, props.modelModifiers)) { value.value = newModelValue; validateField(); diff --git a/packages/vee-validate/src/symbols.ts b/packages/vee-validate/src/symbols.ts index 9b3b4cc04..63953115b 100644 --- a/packages/vee-validate/src/symbols.ts +++ b/packages/vee-validate/src/symbols.ts @@ -12,4 +12,4 @@ export const FormInitialValuesKey: InjectionKey> = Symbol('vee-validate-field-instance'); -export const EMPTY_VALUE = Symbol('Default empty value'); +export const IS_ABSENT = Symbol('Default empty value'); diff --git a/packages/vee-validate/src/utils/assertions.ts b/packages/vee-validate/src/utils/assertions.ts index 88d201415..6da184711 100644 --- a/packages/vee-validate/src/utils/assertions.ts +++ b/packages/vee-validate/src/utils/assertions.ts @@ -1,6 +1,6 @@ import { Locator, YupValidator } from '../types'; import { isCallable, isObject } from '../../../shared'; -import { EMPTY_VALUE } from '../symbols'; +import { IS_ABSENT } from '../symbols'; export function isLocator(value: unknown): value is Locator { return isCallable(value) && !!(value as Locator).__locatorRef; @@ -102,5 +102,5 @@ export function isEvent(evt: unknown): evt is Event { } export function isPropPresent(obj: Record, prop: string) { - return prop in obj && obj[prop] !== EMPTY_VALUE; + return prop in obj && obj[prop] !== IS_ABSENT; } diff --git a/packages/vee-validate/tests/Form.spec.ts b/packages/vee-validate/tests/Form.spec.ts index 4c616497c..4a9e38fd6 100644 --- a/packages/vee-validate/tests/Form.spec.ts +++ b/packages/vee-validate/tests/Form.spec.ts @@ -2177,4 +2177,32 @@ describe('
', () => { expect(fieldError.textContent).toBe(REQUIRED_MESSAGE); expect(meta.textContent).toBe('true'); }); + + // 3424 + test('Checkbox with v-model should not propagate the empty value symbol', async () => { + const value = ref(''); + mountWithHoc({ + setup() { + return { + value, + }; + }, + template: ` + + + + `, + }); + + await flushPromises(); + const input = document.querySelector('input') as HTMLInputElement; + setChecked(input, true); + await flushPromises(); + expect(value.value).toBe('CHECKED'); + + setChecked(input, false); + await flushPromises(); + + expect(value.value).toBe(undefined); + }); });