diff --git a/packages/core/useVModel/index.md b/packages/core/useVModel/index.md index ce2281b45d53..c2fd447f6770 100644 --- a/packages/core/useVModel/index.md +++ b/packages/core/useVModel/index.md @@ -40,6 +40,12 @@ export interface VModelOptions { * @default undefined */ eventName?: string + /** + * Attempting to check for changes of properties in a deeply nested object or array. + * + * @default false + */ + deep?: boolean } /** * Shorthand for v-model binding, props + emit -> ref diff --git a/packages/core/useVModel/index.test.ts b/packages/core/useVModel/index.test.ts index 53333bdb7898..32baca37a60f 100644 --- a/packages/core/useVModel/index.test.ts +++ b/packages/core/useVModel/index.test.ts @@ -1,4 +1,4 @@ -import { isVue2 } from 'vue-demi' +import { isVue2, nextTick } from 'vue-demi' import { useSetup } from '../../.test' import { useVModel } from '.' @@ -35,7 +35,6 @@ describe('useVModel', () => { const data = useVModel(defaultProps(), undefined, emitMock) data.value = 'changed' }) - expect(emitMock.mock.calls[0][0]).toBe(isVue2 ? 'input' : 'update:modelValue') expect(emitMock.mock.calls[0][1]).toBe('changed') }) @@ -48,4 +47,58 @@ describe('useVModel', () => { expect(emitMock.mock.calls[0][0]).toBe('onChange') }) + + it('should emit w/ passive', async() => { + const props = { + ...defaultProps(), + age: 18, + } + useSetup(() => { + const data = useVModel(props, 'age', emitMock, { passive: true }) + data.value = 20 + }) + + await nextTick() + + expect(emitMock.mock.calls[0][0]).toBe('update:age') + expect(emitMock.mock.calls[0][1]).toBe(20) + }) + + it('should emit w/ object props type', async() => { + const props = { + ...defaultProps(), + data: { + age: 18, + }, + } + useSetup(() => { + const data = useVModel(props, 'data', emitMock, { passive: true, deep: true }) + data.value.age = 20 + }) + + await nextTick() + + expect(emitMock.mock.calls[0][0]).toBe('update:data') + expect(emitMock).toHaveBeenCalledTimes(1) + expect(JSON.stringify(emitMock.mock.calls[0][1])).toBe(JSON.stringify({ age: 20 })) + }) + + it('should emit w/ array props type', async() => { + const props = { + ...defaultProps(), + data: { + hobbys: ['coding'], + }, + } + useSetup(() => { + const data = useVModel(props, 'data', emitMock, { passive: true, deep: true }) + data.value.hobbys.push('basketball') + }) + + await nextTick() + + expect(emitMock.mock.calls[0][0]).toBe('update:data') + expect(emitMock).toHaveBeenCalledTimes(1) + expect(JSON.stringify(emitMock.mock.calls[0][1])).toBe(JSON.stringify({ hobbys: ['coding', 'basketball'] })) + }) }) diff --git a/packages/core/useVModel/index.ts b/packages/core/useVModel/index.ts index 254175ae9ce5..4fe0afe050ec 100644 --- a/packages/core/useVModel/index.ts +++ b/packages/core/useVModel/index.ts @@ -1,4 +1,4 @@ -import { computed, getCurrentInstance, isVue2, ref, watch } from 'vue-demi' +import { computed, getCurrentInstance, isVue2, ref, watch, UnwrapRef } from 'vue-demi' export interface VModelOptions { /** @@ -14,6 +14,12 @@ export interface VModelOptions { * @default undefined */ eventName?: string + /** + * Attempting to check for changes of properties in a deeply nested object or array. + * + * @default false + */ + deep?: boolean } /** @@ -33,6 +39,7 @@ export function useVModel

(props[key!]) - watch(() => props[key!], v => proxy.value = v as any) + watch(() => props[key!], v => proxy.value = v as UnwrapRef) + watch(proxy, (v) => { - if (v !== props[key!]) + if (v !== props[key!] || deep) _emit(event, v) + }, { + deep, }) return proxy