Skip to content

Commit 1f2de9e

Browse files
committed
fix(v-model): should use dynamic directive on input with dynamic v-bind
1 parent ae92925 commit 1f2de9e

File tree

6 files changed

+72
-11
lines changed

6 files changed

+72
-11
lines changed

packages/compiler-core/src/utils.ts

+11
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,17 @@ export function findProp(
205205
}
206206
}
207207

208+
export function hasDynamicKeyVBind(node: ElementNode): boolean {
209+
return node.props.some(
210+
p =>
211+
p.type === NodeTypes.DIRECTIVE &&
212+
p.name === 'bind' &&
213+
(!p.arg || // v-bind="obj"
214+
p.arg.type !== NodeTypes.SIMPLE_EXPRESSION || // v-bind:[_ctx.foo]
215+
!p.arg.isStatic) // v-bind:[foo]
216+
)
217+
}
218+
208219
export function createBlockExpression(
209220
blockExp: BlockCodegenNode,
210221
context: TransformContext

packages/compiler-dom/__tests__/transforms/__snapshots__/vModel.spec.ts.snap

+37
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,42 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`compiler: transform v-model input w/ dynamic v-bind 1`] = `
4+
"const _Vue = Vue
5+
6+
return function render() {
7+
with (this) {
8+
const { vModelDynamic: _vModelDynamic, mergeProps: _mergeProps, createVNode: _createVNode, withDirectives: _withDirectives, createBlock: _createBlock, openBlock: _openBlock } = _Vue
9+
10+
return (_openBlock(), _withDirectives(_createBlock(\\"input\\", _mergeProps(obj, {
11+
modelValue: model,
12+
\\"onUpdate:modelValue\\": $event => (model = $event)
13+
}), null, 16 /* FULL_PROPS */, [\\"modelValue\\", \\"onUpdate:modelValue\\"]), [
14+
[_vModelDynamic, model]
15+
]))
16+
}
17+
}"
18+
`;
19+
20+
exports[`compiler: transform v-model input w/ dynamic v-bind 2`] = `
21+
"const _Vue = Vue
22+
23+
return function render() {
24+
with (this) {
25+
const { vModelDynamic: _vModelDynamic, createVNode: _createVNode, withDirectives: _withDirectives, resolveDirective: _resolveDirective, createBlock: _createBlock, openBlock: _openBlock } = _Vue
26+
27+
const _directive_bind = _resolveDirective(\\"bind\\")
28+
29+
return (_openBlock(), _withDirectives(_createBlock(\\"input\\", {
30+
modelValue: model,
31+
\\"onUpdate:modelValue\\": $event => (model = $event)
32+
}, null, 8 /* PROPS */, [\\"modelValue\\", \\"onUpdate:modelValue\\"]), [
33+
[_directive_bind, val, key],
34+
[_vModelDynamic, model]
35+
]))
36+
}
37+
}"
38+
`;
39+
340
exports[`compiler: transform v-model modifiers .lazy 1`] = `
441
"const _Vue = Vue
542

packages/compiler-dom/__tests__/transforms/vModel.spec.ts

+13
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@ describe('compiler: transform v-model', () => {
6363
expect(generate(root).code).toMatchSnapshot()
6464
})
6565

66+
test('input w/ dynamic v-bind', () => {
67+
const root = transformWithModel('<input v-bind="obj" v-model="model" />')
68+
69+
expect(root.helpers).toContain(V_MODEL_DYNAMIC)
70+
expect(generate(root).code).toMatchSnapshot()
71+
72+
const root2 = transformWithModel(
73+
'<input v-bind:[key]="val" v-model="model" />'
74+
)
75+
expect(root2.helpers).toContain(V_MODEL_DYNAMIC)
76+
expect(generate(root2).code).toMatchSnapshot()
77+
})
78+
6679
test('simple expression for select', () => {
6780
const root = transformWithModel('<select v-model="model" />')
6881

packages/compiler-dom/src/transforms/vModel.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import {
33
DirectiveTransform,
44
ElementTypes,
55
findProp,
6-
NodeTypes
6+
NodeTypes,
7+
hasDynamicKeyVBind
78
} from '@vue/compiler-core'
89
import { createDOMCompilerError, DOMErrorCodes } from '../errors'
910
import {
@@ -75,6 +76,10 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
7576
break
7677
}
7778
}
79+
} else if (hasDynamicKeyVBind(node)) {
80+
// element has bindings with dynamic keys, which can possibly contain
81+
// "type".
82+
directiveToUse = V_MODEL_DYNAMIC
7883
} else {
7984
// text type
8085
__DEV__ && checkDuplicatedValue()

packages/compiler-ssr/src/transforms/ssrTransformElement.ts

+3-9
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ import {
1919
JSChildNode,
2020
ArrayExpression,
2121
createAssignmentExpression,
22-
TextNode
22+
TextNode,
23+
hasDynamicKeyVBind
2324
} from '@vue/compiler-dom'
2425
import { escapeHtml, isBooleanAttr, isSSRSafeAttrName } from '@vue/shared'
2526
import { createSSRCompilerError, SSRErrorCodes } from '../errors'
@@ -46,14 +47,7 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
4647
// v-bind="obj" or v-bind:[key] can potentially overwrite other static
4748
// attrs and can affect final rendering result, so when they are present
4849
// we need to bail out to full `renderAttrs`
49-
const hasDynamicVBind = node.props.some(
50-
p =>
51-
p.type === NodeTypes.DIRECTIVE &&
52-
p.name === 'bind' &&
53-
(!p.arg || // v-bind="obj"
54-
p.arg.type !== NodeTypes.SIMPLE_EXPRESSION || // v-bind:[_ctx.foo]
55-
!p.arg.isStatic) // v-bind:[foo]
56-
)
50+
const hasDynamicVBind = hasDynamicKeyVBind(node)
5751
if (hasDynamicVBind) {
5852
const { props } = buildProps(node, context, node.props, true /* ssr */)
5953
if (props) {

packages/compiler-ssr/src/transforms/ssrVModel.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ export const ssrTransformModel: DirectiveTransform = (dir, node, context) => {
9696
checkDuplicatedValue()
9797
node.children = [createInterpolation(model, model.loc)]
9898
} else if (node.tag === 'select') {
99-
// TODO
99+
// NOOP
100+
// select relies on client-side directive to set initial selected state.
100101
} else {
101102
context.onError(
102103
createDOMCompilerError(

0 commit comments

Comments
 (0)