Skip to content

Commit

Permalink
fix: seperate model detection from event emitting closes #3312
Browse files Browse the repository at this point in the history
  • Loading branch information
logaretm committed May 23, 2021
1 parent 1329be3 commit 5e72852
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 14 deletions.
28 changes: 15 additions & 13 deletions packages/vee-validate/src/Field.ts
@@ -1,4 +1,4 @@
import { h, defineComponent, toRef, SetupContext, resolveDynamicComponent, computed, watch } from 'vue';
import { h, defineComponent, toRef, SetupContext, resolveDynamicComponent, computed, watch, PropType } from 'vue';
import { getConfig } from './config';
import { useField } from './useField';
import { normalizeChildren, hasCheckedAttr, shouldHaveValueBinding, isPropPresent } from './utils';
Expand Down Expand Up @@ -69,13 +69,17 @@ export const Field = defineComponent({
type: null,
default: () => ({}),
},
'onUpdate:modelValue': {
type: (null as unknown) as PropType<((e: any) => unknown) | undefined>,
default: undefined,
},
},
emits: ['update:modelValue'],
setup(props, ctx) {
const rules = toRef(props, 'rules');
const name = toRef(props, 'name');
const label = toRef(props, 'label');
const uncheckedValue = toRef(props, 'uncheckedValue');
const hasModelEvents = isPropPresent(props, 'onUpdate:modelValue');

const {
errors,
Expand Down Expand Up @@ -104,14 +108,14 @@ export const Field = defineComponent({
});

// If there is a v-model applied on the component we need to emit the `update:modelValue` whenever the value binding changes
const onChangeHandler = isPropPresent(props, 'modelValue')
const onChangeHandler = hasModelEvents
? function handleChangeWithModel(e: unknown, shouldValidate = true) {
handleChange(e, shouldValidate);
ctx.emit('update:modelValue', value.value);
}
: handleChange;

const onInputHandler = isPropPresent(props, 'modelValue')
const onInputHandler = hasModelEvents
? function handleChangeWithModel(e: any) {
handleInput(e);
ctx.emit('update:modelValue', value.value);
Expand Down Expand Up @@ -151,15 +155,13 @@ export const Field = defineComponent({
return attrs;
});

if (isPropPresent(props, 'modelValue')) {
const modelValue = toRef(props, 'modelValue');
watch(modelValue, newModelValue => {
if (newModelValue !== applyModifiers(value.value, props.modelModifiers)) {
value.value = newModelValue;
validateField();
}
});
}
const modelValue = toRef(props, 'modelValue');
watch(modelValue, newModelValue => {
if (newModelValue !== applyModifiers(value.value, props.modelModifiers)) {
value.value = newModelValue;
validateField();
}
});

function slotProps() {
return {
Expand Down
24 changes: 23 additions & 1 deletion packages/vee-validate/tests/Field.spec.ts
Expand Up @@ -2,7 +2,7 @@ import flushPromises from 'flush-promises';
import { defineRule, configure } from '@/vee-validate';
import { mountWithHoc, setValue, dispatchEvent, setChecked } from './helpers';
import * as yup from 'yup';
import { ref, Ref } from 'vue';
import { reactive, ref, Ref } from 'vue';

jest.useFakeTimers();

Expand Down Expand Up @@ -985,4 +985,26 @@ describe('<Field />', () => {
await flushPromises();
expect(error.textContent).toBe(errorMessage);
});

// #3312
test('v-model on a non-existent nested prop should still emit model events', async () => {
const form = reactive({});
const wrapper = mountWithHoc({
setup() {
return { form };
},
template: `
<div>
<Field v-model="form.field" name="field" />
</div>
`,
});

await flushPromises();
const input = wrapper.$el.querySelector('input');
input.value = 'hello';
dispatchEvent(input, 'input');
await flushPromises();
expect((form as any).field).toBe('hello');
});
});

0 comments on commit 5e72852

Please sign in to comment.