]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-ssr): fix ssr compile error for select with non-option children (#9442)
authoredison <daiwei521@126.com>
Fri, 20 Oct 2023 12:39:31 +0000 (20:39 +0800)
committerGitHub <noreply@github.com>
Fri, 20 Oct 2023 12:39:31 +0000 (20:39 +0800)
fix #9440

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

index 5bccbcb788c13a5fa38a94358dc1d698488e4cd2..f28c38d402666c0c5f577aa7ec7efd916b4c870b 100644 (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">', () => {
index bd587edcb9cab664041c74f37ad4fbfd57a73686..0c4bd17787533adb843923bc3db085ade6c67767 100644 (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 {