diff --git a/packages/runtime-dom/src/directives/vModel.ts b/packages/runtime-dom/src/directives/vModel.ts
index 1a14decd..722b4d9b 100644
--- a/packages/runtime-dom/src/directives/vModel.ts
+++ b/packages/runtime-dom/src/directives/vModel.ts
@@ -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(
el: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement,
binding: DirectiveBinding,
@@ -276,26 +294,10 @@ function callModelHook(
prevVNode: VNode | null,
hook: keyof ObjectDirective
) {
- let modelToUse: ObjectDirective
- switch (el.tagName) {
- case 'SELECT':
- 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 modelToUse = resolveDynamicModel(
+ el.tagName,
+ vnode.props && vnode.props.type
+ )
const fn = modelToUse[hook] as DirectiveHook
fn && fn(el, binding, vnode, prevVNode)
}
@@ -324,4 +326,18 @@ export function initVModelForSSR() {
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)
+ }
+ }
}
diff --git a/packages/server-renderer/__tests__/ssrDirectives.spec.ts b/packages/server-renderer/__tests__/ssrDirectives.spec.ts
index 3e8bd2e0..74b01204 100644
--- a/packages/server-renderer/__tests__/ssrDirectives.spec.ts
+++ b/packages/server-renderer/__tests__/ssrDirectives.spec.ts
@@ -11,6 +11,7 @@ import {
vModelText,
vModelRadio,
vModelCheckbox,
+ vModelDynamic,
resolveDirective
} from 'vue'
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(``)
+ })
+
+ test('radio', async () => {
+ expect(
+ await renderToString(
+ createApp({
+ render() {
+ return withDirectives(
+ h('input', { type: 'radio', value: 'hello' }),
+ [[vModelDynamic, 'hello']]
+ )
+ }
+ })
+ )
+ ).toBe(``)
+
+ expect(
+ await renderToString(
+ createApp({
+ render() {
+ return withDirectives(
+ h('input', { type: 'radio', value: 'hello' }),
+ [[vModelDynamic, 'foo']]
+ )
+ }
+ })
+ )
+ ).toBe(``)
+ })
+
+ test('checkbox', async () => {
+ expect(
+ await renderToString(
+ createApp({
+ render() {
+ return withDirectives(h('input', { type: 'checkbox' }), [
+ [vModelDynamic, true]
+ ])
+ }
+ })
+ )
+ ).toBe(``)
+
+ expect(
+ await renderToString(
+ createApp({
+ render() {
+ return withDirectives(h('input', { type: 'checkbox' }), [
+ [vModelDynamic, false]
+ ])
+ }
+ })
+ )
+ ).toBe(``)
+
+ expect(
+ await renderToString(
+ createApp({
+ render() {
+ return withDirectives(
+ h('input', { type: 'checkbox', value: 'foo' }),
+ [[vModelDynamic, ['foo']]]
+ )
+ }
+ })
+ )
+ ).toBe(``)
+
+ expect(
+ await renderToString(
+ createApp({
+ render() {
+ return withDirectives(
+ h('input', { type: 'checkbox', value: 'foo' }),
+ [[vModelDynamic, []]]
+ )
+ }
+ })
+ )
+ ).toBe(``)
+ })
+ })
+
test('custom directive w/ getSSRProps (vdom)', async () => {
expect(
await renderToString(