Skip to content

Commit

Permalink
feat(useVModel): new options deep to emit for deep changes (vitest-…
Browse files Browse the repository at this point in the history
…dev#669)

Co-authored-by: webfansplz <>
  • Loading branch information
webfansplz committed Aug 24, 2021
1 parent 9548a08 commit db319af
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 5 deletions.
6 changes: 6 additions & 0 deletions packages/core/useVModel/index.md
Expand Up @@ -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
Expand Down
57 changes: 55 additions & 2 deletions 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 '.'

Expand Down Expand Up @@ -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')
})
Expand All @@ -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'] }))
})
})
16 changes: 13 additions & 3 deletions 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 {
/**
Expand All @@ -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
}

/**
Expand All @@ -33,6 +39,7 @@ export function useVModel<P extends object, K extends keyof P, Name extends stri
const {
passive = false,
eventName,
deep = false,
} = options

const vm = getCurrentInstance()
Expand All @@ -56,10 +63,13 @@ export function useVModel<P extends object, K extends keyof P, Name extends stri
if (passive) {
const proxy = ref<P[K]>(props[key!])

watch(() => props[key!], v => proxy.value = v as any)
watch(() => props[key!], v => proxy.value = v as UnwrapRef<P[K]>)

watch(proxy, (v) => {
if (v !== props[key!])
if (v !== props[key!] || deep)
_emit(event, v)
}, {
deep,
})

return proxy
Expand Down

0 comments on commit db319af

Please sign in to comment.