]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(ssr): handle initial selected state for select with v-model + v-for option (...
authoredison <daiwei521@126.com>
Fri, 15 Nov 2024 14:32:39 +0000 (22:32 +0800)
committerGitHub <noreply@github.com>
Fri, 15 Nov 2024 14:32:39 +0000 (22:32 +0800)
close #12395

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

index f2c04f0a5e8b71287ccc458b12cd89b41e5c61cc..0bf7673d00dc1c3bc3a6daabe80a9b079020719f 100644 (file)
@@ -52,6 +52,52 @@ describe('ssr: v-model', () => {
       }"
     `)
 
+    expect(
+      compileWithWrapper(
+        `<select v-model="model"><option v-for="i in items" :value="i"></option></select>`,
+      ).code,
+    ).toMatchInlineSnapshot(`
+      "const { ssrRenderAttr: _ssrRenderAttr, ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseContain: _ssrLooseContain, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs, ssrRenderList: _ssrRenderList } = require("vue/server-renderer")
+
+      return function ssrRender(_ctx, _push, _parent, _attrs) {
+        _push(\`<div\${_ssrRenderAttrs(_attrs)}><select><!--[-->\`)
+        _ssrRenderList(_ctx.items, (i) => {
+          _push(\`<option\${
+            _ssrRenderAttr("value", i)
+          }\${
+            (_ssrIncludeBooleanAttr((Array.isArray(_ctx.model))
+              ? _ssrLooseContain(_ctx.model, i)
+              : _ssrLooseEqual(_ctx.model, i))) ? " selected" : ""
+          }></option>\`)
+        })
+        _push(\`<!--]--></select></div>\`)
+      }"
+    `)
+
+    expect(
+      compileWithWrapper(
+        `<select v-model="model"><option v-if="true" :value="i"></option></select>`,
+      ).code,
+    ).toMatchInlineSnapshot(`
+      "const { ssrRenderAttr: _ssrRenderAttr, ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseContain: _ssrLooseContain, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs } = require("vue/server-renderer")
+
+      return function ssrRender(_ctx, _push, _parent, _attrs) {
+        _push(\`<div\${_ssrRenderAttrs(_attrs)}><select>\`)
+        if (true) {
+          _push(\`<option\${
+            _ssrRenderAttr("value", _ctx.i)
+          }\${
+            (_ssrIncludeBooleanAttr((Array.isArray(_ctx.model))
+              ? _ssrLooseContain(_ctx.model, _ctx.i)
+              : _ssrLooseEqual(_ctx.model, _ctx.i))) ? " selected" : ""
+          }></option>\`)
+        } else {
+          _push(\`<!---->\`)
+        }
+        _push(\`</select></div>\`)
+      }"
+    `)
+
     expect(
       compileWithWrapper(
         `<select multiple v-model="model"><option value="1" selected></option><option value="2"></option></select>`,
index 9a645397e1a0aa1028e89d69cfb9139c22337aad..80e3839318b1540396ed8db2e0b8a9b6e5d49924 100644 (file)
@@ -5,6 +5,7 @@ import {
   type ExpressionNode,
   NodeTypes,
   type PlainElementNode,
+  type TemplateChildNode,
   createCallExpression,
   createConditionalExpression,
   createDOMCompilerError,
@@ -162,11 +163,18 @@ export const ssrTransformModel: DirectiveTransform = (dir, node, context) => {
       checkDuplicatedValue()
       node.children = [createInterpolation(model, model.loc)]
     } else if (node.tag === 'select') {
-      node.children.forEach(child => {
-        if (child.type === NodeTypes.ELEMENT) {
-          processOption(child as PlainElementNode)
-        }
-      })
+      const processChildren = (children: TemplateChildNode[]) => {
+        children.forEach(child => {
+          if (child.type === NodeTypes.ELEMENT) {
+            processOption(child as PlainElementNode)
+          } else if (child.type === NodeTypes.FOR) {
+            processChildren(child.children)
+          } else if (child.type === NodeTypes.IF) {
+            child.branches.forEach(b => processChildren(b.children))
+          }
+        })
+      }
+      processChildren(node.children)
     } else {
       context.onError(
         createDOMCompilerError(