fix(ssr): render correct initial selected state for select with v-model (#7432)
close #7392
This commit is contained in:
parent
3decc57d0c
commit
201c46df07
|
@ -33,6 +33,44 @@ describe('ssr: v-model', () => {
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('<select v-model>', () => {
|
||||||
|
expect(
|
||||||
|
compileWithWrapper(
|
||||||
|
`<select v-model="model"><option value="1"></option></select>`
|
||||||
|
).code
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
"const { ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseContain: _ssrLooseContain, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
|
||||||
|
|
||||||
|
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||||
|
_push(\`<div\${
|
||||||
|
_ssrRenderAttrs(_attrs)
|
||||||
|
}><select><option value=\\"1\\"\${
|
||||||
|
(_ssrIncludeBooleanAttr((Array.isArray(_ctx.model))
|
||||||
|
? _ssrLooseContain(_ctx.model, \\"1\\")
|
||||||
|
: _ssrLooseEqual(_ctx.model, \\"1\\"))) ? \\" selected\\" : \\"\\"
|
||||||
|
}></option></select></div>\`)
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
compileWithWrapper(
|
||||||
|
`<select multiple v-model="model"><option value="1" selected></option><option value="2"></option></select>`
|
||||||
|
).code
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
"const { ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseContain: _ssrLooseContain, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
|
||||||
|
|
||||||
|
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||||
|
_push(\`<div\${
|
||||||
|
_ssrRenderAttrs(_attrs)
|
||||||
|
}><select multiple><option value=\\"1\\" selected></option><option value=\\"2\\"\${
|
||||||
|
(_ssrIncludeBooleanAttr((Array.isArray(_ctx.model))
|
||||||
|
? _ssrLooseContain(_ctx.model, \\"2\\")
|
||||||
|
: _ssrLooseEqual(_ctx.model, \\"2\\"))) ? \\" selected\\" : \\"\\"
|
||||||
|
}></option></select></div>\`)
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
test('<input type="radio">', () => {
|
test('<input type="radio">', () => {
|
||||||
expect(
|
expect(
|
||||||
compileWithWrapper(`<input type="radio" value="foo" v-model="bar">`).code
|
compileWithWrapper(`<input type="radio" value="foo" v-model="bar">`).code
|
||||||
|
|
|
@ -18,7 +18,8 @@ import {
|
||||||
import {
|
import {
|
||||||
SSR_LOOSE_EQUAL,
|
SSR_LOOSE_EQUAL,
|
||||||
SSR_LOOSE_CONTAIN,
|
SSR_LOOSE_CONTAIN,
|
||||||
SSR_RENDER_DYNAMIC_MODEL
|
SSR_RENDER_DYNAMIC_MODEL,
|
||||||
|
SSR_INCLUDE_BOOLEAN_ATTR
|
||||||
} from '../runtimeHelpers'
|
} from '../runtimeHelpers'
|
||||||
import { DirectiveTransformResult } from 'packages/compiler-core/src/transform'
|
import { DirectiveTransformResult } from 'packages/compiler-core/src/transform'
|
||||||
|
|
||||||
|
@ -129,8 +130,34 @@ export const ssrTransformModel: DirectiveTransform = (dir, node, context) => {
|
||||||
checkDuplicatedValue()
|
checkDuplicatedValue()
|
||||||
node.children = [createInterpolation(model, model.loc)]
|
node.children = [createInterpolation(model, model.loc)]
|
||||||
} else if (node.tag === 'select') {
|
} else if (node.tag === 'select') {
|
||||||
// NOOP
|
node.children.forEach(option => {
|
||||||
// select relies on client-side directive to set initial selected state.
|
if (option.type === NodeTypes.ELEMENT) {
|
||||||
|
const plainNode = option as PlainElementNode
|
||||||
|
if (plainNode.props.findIndex(p => p.name === 'selected') === -1) {
|
||||||
|
const value = findValueBinding(plainNode)
|
||||||
|
plainNode.ssrCodegenNode!.elements.push(
|
||||||
|
createConditionalExpression(
|
||||||
|
createCallExpression(context.helper(SSR_INCLUDE_BOOLEAN_ATTR), [
|
||||||
|
createConditionalExpression(
|
||||||
|
createCallExpression(`Array.isArray`, [model]),
|
||||||
|
createCallExpression(context.helper(SSR_LOOSE_CONTAIN), [
|
||||||
|
model,
|
||||||
|
value
|
||||||
|
]),
|
||||||
|
createCallExpression(context.helper(SSR_LOOSE_EQUAL), [
|
||||||
|
model,
|
||||||
|
value
|
||||||
|
])
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
createSimpleExpression(' selected', true),
|
||||||
|
createSimpleExpression('', true),
|
||||||
|
false /* no newline */
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
context.onError(
|
context.onError(
|
||||||
createDOMCompilerError(
|
createDOMCompilerError(
|
||||||
|
|
|
@ -107,6 +107,30 @@ describe('ssr: directives', () => {
|
||||||
).toBe(`<input type="radio">`)
|
).toBe(`<input type="radio">`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('select', async () => {
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
data: () => ({ model: 1 }),
|
||||||
|
template: `<select v-model="model"><option value="0"></option><option value="1"></option></select>`
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(
|
||||||
|
`<select><option value="0"></option><option value="1" selected></option></select>`
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await renderToString(
|
||||||
|
createApp({
|
||||||
|
data: () => ({ model: [0, 1] }),
|
||||||
|
template: `<select multiple v-model="model"><option value="0"></option><option value="1"></option></select>`
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe(
|
||||||
|
`<select multiple><option value="0" selected></option><option value="1" selected></option></select>`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
test('checkbox', async () => {
|
test('checkbox', async () => {
|
||||||
expect(
|
expect(
|
||||||
await renderToString(
|
await renderToString(
|
||||||
|
|
Loading…
Reference in New Issue