fix(compiler-ssr): fix ssr compile error for select with non-option children (#9442)

fix #9440
This commit is contained in:
edison 2023-10-20 20:39:31 +08:00 committed by GitHub
parent aacb66def4
commit cdb2e725e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 86 additions and 26 deletions

View File

@ -69,6 +69,57 @@ describe('ssr: v-model', () => {
}></option></select></div>\`)
}"
`)
expect(
compileWithWrapper(`<select multiple v-model="model"><slot/></select>`)
.code
).toMatchInlineSnapshot(`
"const { ssrRenderSlot: _ssrRenderSlot, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
return function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`<div\${_ssrRenderAttrs(_attrs)}><select multiple>\`)
_ssrRenderSlot(_ctx.$slots, \\"default\\", {}, null, _push, _parent)
_push(\`</select></div>\`)
}"
`)
expect(
compileWithWrapper(`
<select multiple v-model="model">
<optgroup label="foo">
<option value="bar">bar</option>
</optgroup>
</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><optgroup label=\\"foo\\"><option value=\\"bar\\"\${
(_ssrIncludeBooleanAttr((Array.isArray(_ctx.model))
? _ssrLooseContain(_ctx.model, \\"bar\\")
: _ssrLooseEqual(_ctx.model, \\"bar\\"))) ? \\" selected\\" : \\"\\"
}>bar</option></optgroup></select></div>\`)
}"
`)
expect(
compileWithWrapper(`
<select multiple v-model="model">
<optgroup label="foo">
<slot/>
</optgroup>
</select>`).code
).toMatchInlineSnapshot(`
"const { ssrRenderSlot: _ssrRenderSlot, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
return function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`<div\${_ssrRenderAttrs(_attrs)}><select multiple><optgroup label=\\"foo\\">\`)
_ssrRenderSlot(_ctx.$slots, \\"default\\", {}, null, _push, _parent)
_push(\`</optgroup></select></div>\`)
}"
`)
})
test('<input type="radio">', () => {

View File

@ -38,6 +38,38 @@ export const ssrTransformModel: DirectiveTransform = (dir, node, context) => {
}
}
function processOption(plainNode: PlainElementNode) {
if (plainNode.tag === 'option') {
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 if (plainNode.tag === 'optgroup') {
plainNode.children.forEach(option =>
processOption(option as PlainElementNode)
)
}
}
if (node.tagType === ElementTypes.ELEMENT) {
const res: DirectiveTransformResult = { props: [] }
const defaultProps = [
@ -130,32 +162,9 @@ export const ssrTransformModel: DirectiveTransform = (dir, node, context) => {
checkDuplicatedValue()
node.children = [createInterpolation(model, model.loc)]
} else if (node.tag === 'select') {
node.children.forEach(option => {
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 */
)
)
}
node.children.forEach(child => {
if (child.type === NodeTypes.ELEMENT) {
processOption(child as PlainElementNode)
}
})
} else {