fix(ssr): support client-compiled v-model with dynamic type during ssr (#5787)
fix #5786
This commit is contained in:
parent
847d7f782b
commit
c03459b9b6
@ -269,6 +269,24 @@ export const vModelDynamic: ObjectDirective<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveDynamicModel(tagName: string, type: string | undefined) {
|
||||||
|
switch (tagName) {
|
||||||
|
case 'SELECT':
|
||||||
|
return vModelSelect
|
||||||
|
case 'TEXTAREA':
|
||||||
|
return vModelText
|
||||||
|
default:
|
||||||
|
switch (type) {
|
||||||
|
case 'checkbox':
|
||||||
|
return vModelCheckbox
|
||||||
|
case 'radio':
|
||||||
|
return vModelRadio
|
||||||
|
default:
|
||||||
|
return vModelText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function callModelHook(
|
function callModelHook(
|
||||||
el: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement,
|
el: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement,
|
||||||
binding: DirectiveBinding,
|
binding: DirectiveBinding,
|
||||||
@ -276,26 +294,10 @@ function callModelHook(
|
|||||||
prevVNode: VNode | null,
|
prevVNode: VNode | null,
|
||||||
hook: keyof ObjectDirective
|
hook: keyof ObjectDirective
|
||||||
) {
|
) {
|
||||||
let modelToUse: ObjectDirective
|
const modelToUse = resolveDynamicModel(
|
||||||
switch (el.tagName) {
|
el.tagName,
|
||||||
case 'SELECT':
|
vnode.props && vnode.props.type
|
||||||
modelToUse = vModelSelect
|
)
|
||||||
break
|
|
||||||
case 'TEXTAREA':
|
|
||||||
modelToUse = vModelText
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
switch (vnode.props && vnode.props.type) {
|
|
||||||
case 'checkbox':
|
|
||||||
modelToUse = vModelCheckbox
|
|
||||||
break
|
|
||||||
case 'radio':
|
|
||||||
modelToUse = vModelRadio
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
modelToUse = vModelText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const fn = modelToUse[hook] as DirectiveHook
|
const fn = modelToUse[hook] as DirectiveHook
|
||||||
fn && fn(el, binding, vnode, prevVNode)
|
fn && fn(el, binding, vnode, prevVNode)
|
||||||
}
|
}
|
||||||
@ -324,4 +326,18 @@ export function initVModelForSSR() {
|
|||||||
return { checked: true }
|
return { checked: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vModelDynamic.getSSRProps = (binding, vnode) => {
|
||||||
|
if (typeof vnode.type !== 'string') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const modelToUse = resolveDynamicModel(
|
||||||
|
// resolveDynamicModel expects an uppercase tag name, but vnode.type is lowercase
|
||||||
|
vnode.type.toUpperCase(),
|
||||||
|
vnode.props && vnode.props.type
|
||||||
|
)
|
||||||
|
if (modelToUse.getSSRProps) {
|
||||||
|
return modelToUse.getSSRProps(binding, vnode)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
vModelText,
|
vModelText,
|
||||||
vModelRadio,
|
vModelRadio,
|
||||||
vModelCheckbox,
|
vModelCheckbox,
|
||||||
|
vModelDynamic,
|
||||||
resolveDirective
|
resolveDirective
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import { ssrGetDirectiveProps, ssrRenderAttrs } from '../src'
|
import { ssrGetDirectiveProps, ssrRenderAttrs } from '../src'
|
||||||
@ -376,6 +377,100 @@ describe('ssr: directives', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('vnode v-model dynamic', () => {
|
||||||
|
test('text', async () => {
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
render() {
|
||||||
|
return withDirectives(h('input'), [[vModelDynamic, 'hello']])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(`<input value="hello">`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('radio', async () => {
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
render() {
|
||||||
|
return withDirectives(
|
||||||
|
h('input', { type: 'radio', value: 'hello' }),
|
||||||
|
[[vModelDynamic, 'hello']]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(`<input type="radio" value="hello" checked>`)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
render() {
|
||||||
|
return withDirectives(
|
||||||
|
h('input', { type: 'radio', value: 'hello' }),
|
||||||
|
[[vModelDynamic, 'foo']]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(`<input type="radio" value="hello">`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('checkbox', async () => {
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
render() {
|
||||||
|
return withDirectives(h('input', { type: 'checkbox' }), [
|
||||||
|
[vModelDynamic, true]
|
||||||
|
])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(`<input type="checkbox" checked>`)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
render() {
|
||||||
|
return withDirectives(h('input', { type: 'checkbox' }), [
|
||||||
|
[vModelDynamic, false]
|
||||||
|
])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(`<input type="checkbox">`)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
render() {
|
||||||
|
return withDirectives(
|
||||||
|
h('input', { type: 'checkbox', value: 'foo' }),
|
||||||
|
[[vModelDynamic, ['foo']]]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(`<input type="checkbox" value="foo" checked>`)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
render() {
|
||||||
|
return withDirectives(
|
||||||
|
h('input', { type: 'checkbox', value: 'foo' }),
|
||||||
|
[[vModelDynamic, []]]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(`<input type="checkbox" value="foo">`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
test('custom directive w/ getSSRProps (vdom)', async () => {
|
test('custom directive w/ getSSRProps (vdom)', async () => {
|
||||||
expect(
|
expect(
|
||||||
await renderToString(
|
await renderToString(
|
||||||
|
Loading…
Reference in New Issue
Block a user