fix(defineModel): support kebab-case/camelCase mismatches (#9950)

This commit is contained in:
skirtle 2024-01-03 10:18:35 +00:00 committed by GitHub
parent f300a4001e
commit 10ccb9bfa0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 85 additions and 2 deletions

View File

@ -314,6 +314,84 @@ describe('SFC <script setup> helpers', () => {
expect(serializeInner(root)).toBe('bar')
})
test('kebab-case v-model (should not be local)', async () => {
let foo: any
const compRender = vi.fn()
const Comp = defineComponent({
props: ['fooBar'],
emits: ['update:fooBar'],
setup(props) {
foo = useModel(props, 'fooBar')
return () => {
compRender()
return foo.value
}
},
})
const updateFooBar = vi.fn()
const root = nodeOps.createElement('div')
// v-model:foo-bar compiles to foo-bar and onUpdate:fooBar
render(
h(Comp, { 'foo-bar': 'initial', 'onUpdate:fooBar': updateFooBar }),
root,
)
expect(compRender).toBeCalledTimes(1)
expect(serializeInner(root)).toBe('initial')
expect(foo.value).toBe('initial')
foo.value = 'bar'
// should not be using local mode, so nothing should actually change
expect(foo.value).toBe('initial')
await nextTick()
expect(compRender).toBeCalledTimes(1)
expect(updateFooBar).toBeCalledTimes(1)
expect(updateFooBar).toHaveBeenCalledWith('bar')
expect(foo.value).toBe('initial')
expect(serializeInner(root)).toBe('initial')
})
test('kebab-case update listener (should not be local)', async () => {
let foo: any
const compRender = vi.fn()
const Comp = defineComponent({
props: ['fooBar'],
emits: ['update:fooBar'],
setup(props) {
foo = useModel(props, 'fooBar')
return () => {
compRender()
return foo.value
}
},
})
const updateFooBar = vi.fn()
const root = nodeOps.createElement('div')
// The template compiler won't create hyphenated listeners, but it could have been passed manually
render(
h(Comp, { 'foo-bar': 'initial', 'onUpdate:foo-bar': updateFooBar }),
root,
)
expect(compRender).toBeCalledTimes(1)
expect(serializeInner(root)).toBe('initial')
expect(foo.value).toBe('initial')
foo.value = 'bar'
// should not be using local mode, so nothing should actually change
expect(foo.value).toBe('initial')
await nextTick()
expect(compRender).toBeCalledTimes(1)
expect(updateFooBar).toBeCalledTimes(1)
expect(updateFooBar).toHaveBeenCalledWith('bar')
expect(foo.value).toBe('initial')
expect(serializeInner(root)).toBe('initial')
})
test('default value', async () => {
let count: any
const inc = () => {

View File

@ -6,6 +6,7 @@ import {
camelize,
extend,
hasChanged,
hyphenate,
isArray,
isFunction,
isPromise,
@ -382,6 +383,7 @@ export function useModel(
}
const camelizedName = camelize(name)
const hyphenatedName = hyphenate(name)
const res = customRef((track, trigger) => {
let localValue: any
@ -403,9 +405,12 @@ export function useModel(
!(
rawProps &&
// check if parent has passed v-model
(name in rawProps || camelizedName in rawProps) &&
(name in rawProps ||
camelizedName in rawProps ||
hyphenatedName in rawProps) &&
(`onUpdate:${name}` in rawProps ||
`onUpdate:${camelizedName}` in rawProps)
`onUpdate:${camelizedName}` in rawProps ||
`onUpdate:${hyphenatedName}` in rawProps)
) &&
hasChanged(value, localValue)
) {