diff --git a/packages/compiler-core/src/utils.ts b/packages/compiler-core/src/utils.ts
index 78e952e4..d160e96e 100644
--- a/packages/compiler-core/src/utils.ts
+++ b/packages/compiler-core/src/utils.ts
@@ -205,6 +205,17 @@ export function findProp(
}
}
+export function hasDynamicKeyVBind(node: ElementNode): boolean {
+ return node.props.some(
+ p =>
+ p.type === NodeTypes.DIRECTIVE &&
+ p.name === 'bind' &&
+ (!p.arg || // v-bind="obj"
+ p.arg.type !== NodeTypes.SIMPLE_EXPRESSION || // v-bind:[_ctx.foo]
+ !p.arg.isStatic) // v-bind:[foo]
+ )
+}
+
export function createBlockExpression(
blockExp: BlockCodegenNode,
context: TransformContext
diff --git a/packages/compiler-dom/__tests__/transforms/__snapshots__/vModel.spec.ts.snap b/packages/compiler-dom/__tests__/transforms/__snapshots__/vModel.spec.ts.snap
index f996e955..88bba3b0 100644
--- a/packages/compiler-dom/__tests__/transforms/__snapshots__/vModel.spec.ts.snap
+++ b/packages/compiler-dom/__tests__/transforms/__snapshots__/vModel.spec.ts.snap
@@ -1,5 +1,42 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`compiler: transform v-model input w/ dynamic v-bind 1`] = `
+"const _Vue = Vue
+
+return function render() {
+ with (this) {
+ const { vModelDynamic: _vModelDynamic, mergeProps: _mergeProps, createVNode: _createVNode, withDirectives: _withDirectives, createBlock: _createBlock, openBlock: _openBlock } = _Vue
+
+ return (_openBlock(), _withDirectives(_createBlock(\\"input\\", _mergeProps(obj, {
+ modelValue: model,
+ \\"onUpdate:modelValue\\": $event => (model = $event)
+ }), null, 16 /* FULL_PROPS */, [\\"modelValue\\", \\"onUpdate:modelValue\\"]), [
+ [_vModelDynamic, model]
+ ]))
+ }
+}"
+`;
+
+exports[`compiler: transform v-model input w/ dynamic v-bind 2`] = `
+"const _Vue = Vue
+
+return function render() {
+ with (this) {
+ const { vModelDynamic: _vModelDynamic, createVNode: _createVNode, withDirectives: _withDirectives, resolveDirective: _resolveDirective, createBlock: _createBlock, openBlock: _openBlock } = _Vue
+
+ const _directive_bind = _resolveDirective(\\"bind\\")
+
+ return (_openBlock(), _withDirectives(_createBlock(\\"input\\", {
+ modelValue: model,
+ \\"onUpdate:modelValue\\": $event => (model = $event)
+ }, null, 8 /* PROPS */, [\\"modelValue\\", \\"onUpdate:modelValue\\"]), [
+ [_directive_bind, val, key],
+ [_vModelDynamic, model]
+ ]))
+ }
+}"
+`;
+
exports[`compiler: transform v-model modifiers .lazy 1`] = `
"const _Vue = Vue
diff --git a/packages/compiler-dom/__tests__/transforms/vModel.spec.ts b/packages/compiler-dom/__tests__/transforms/vModel.spec.ts
index a70e117a..acf9cb95 100644
--- a/packages/compiler-dom/__tests__/transforms/vModel.spec.ts
+++ b/packages/compiler-dom/__tests__/transforms/vModel.spec.ts
@@ -63,6 +63,19 @@ describe('compiler: transform v-model', () => {
expect(generate(root).code).toMatchSnapshot()
})
+ test('input w/ dynamic v-bind', () => {
+ const root = transformWithModel('')
+
+ expect(root.helpers).toContain(V_MODEL_DYNAMIC)
+ expect(generate(root).code).toMatchSnapshot()
+
+ const root2 = transformWithModel(
+ ''
+ )
+ expect(root2.helpers).toContain(V_MODEL_DYNAMIC)
+ expect(generate(root2).code).toMatchSnapshot()
+ })
+
test('simple expression for select', () => {
const root = transformWithModel('')
diff --git a/packages/compiler-dom/src/transforms/vModel.ts b/packages/compiler-dom/src/transforms/vModel.ts
index 83d623f5..e82ad780 100644
--- a/packages/compiler-dom/src/transforms/vModel.ts
+++ b/packages/compiler-dom/src/transforms/vModel.ts
@@ -3,7 +3,8 @@ import {
DirectiveTransform,
ElementTypes,
findProp,
- NodeTypes
+ NodeTypes,
+ hasDynamicKeyVBind
} from '@vue/compiler-core'
import { createDOMCompilerError, DOMErrorCodes } from '../errors'
import {
@@ -75,6 +76,10 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
break
}
}
+ } else if (hasDynamicKeyVBind(node)) {
+ // element has bindings with dynamic keys, which can possibly contain
+ // "type".
+ directiveToUse = V_MODEL_DYNAMIC
} else {
// text type
__DEV__ && checkDuplicatedValue()
diff --git a/packages/compiler-ssr/src/transforms/ssrTransformElement.ts b/packages/compiler-ssr/src/transforms/ssrTransformElement.ts
index 4f764410..feacbd89 100644
--- a/packages/compiler-ssr/src/transforms/ssrTransformElement.ts
+++ b/packages/compiler-ssr/src/transforms/ssrTransformElement.ts
@@ -19,7 +19,8 @@ import {
JSChildNode,
ArrayExpression,
createAssignmentExpression,
- TextNode
+ TextNode,
+ hasDynamicKeyVBind
} from '@vue/compiler-dom'
import { escapeHtml, isBooleanAttr, isSSRSafeAttrName } from '@vue/shared'
import { createSSRCompilerError, SSRErrorCodes } from '../errors'
@@ -46,14 +47,7 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
// v-bind="obj" or v-bind:[key] can potentially overwrite other static
// attrs and can affect final rendering result, so when they are present
// we need to bail out to full `renderAttrs`
- const hasDynamicVBind = node.props.some(
- p =>
- p.type === NodeTypes.DIRECTIVE &&
- p.name === 'bind' &&
- (!p.arg || // v-bind="obj"
- p.arg.type !== NodeTypes.SIMPLE_EXPRESSION || // v-bind:[_ctx.foo]
- !p.arg.isStatic) // v-bind:[foo]
- )
+ const hasDynamicVBind = hasDynamicKeyVBind(node)
if (hasDynamicVBind) {
const { props } = buildProps(node, context, node.props, true /* ssr */)
if (props) {
diff --git a/packages/compiler-ssr/src/transforms/ssrVModel.ts b/packages/compiler-ssr/src/transforms/ssrVModel.ts
index 8d4c0ad6..61933fe9 100644
--- a/packages/compiler-ssr/src/transforms/ssrVModel.ts
+++ b/packages/compiler-ssr/src/transforms/ssrVModel.ts
@@ -96,7 +96,8 @@ export const ssrTransformModel: DirectiveTransform = (dir, node, context) => {
checkDuplicatedValue()
node.children = [createInterpolation(model, model.loc)]
} else if (node.tag === 'select') {
- // TODO
+ // NOOP
+ // select relies on client-side directive to set initial selected state.
} else {
context.onError(
createDOMCompilerError(