Преглед изворни кода

support v-for on scoped slots (fix #5615)

Evan You пре 9 година
родитељ
комит
0ccefff794

+ 1 - 1
flow/component.js

@@ -106,7 +106,7 @@ declare interface Component {
   // check custom keyCode
   _k: (eventKeyCode: number, key: string, builtInAlias: number | Array<number> | void) => boolean;
   // resolve scoped slots
-  _u: (scopedSlots: Array<[string, Function]>) => { [key: string]: Function };
+  _u: (scopedSlots: ScopedSlotsData, res?: Object) => { [key: string]: Function };
 
   // allow dynamic method registration
   [key: string]: any

+ 3 - 1
flow/vnode.js

@@ -71,4 +71,6 @@ declare type VNodeDirective = {
   arg?: string;
   modifiers?: ASTModifiers;
   def?: Object;
-}
+};
+
+declare type ScopedSlotsData = Array<{ key: string, fn: Function } | ScopedSlotsData>;

+ 17 - 2
src/compiler/codegen/index.js

@@ -298,11 +298,26 @@ function genScopedSlots (slots: { [key: string]: ASTElement }): string {
 }
 
 function genScopedSlot (key: string, el: ASTElement) {
-  return `[${key},function(${String(el.attrsMap.scope)}){` +
+  if (el.for && !el.forProcessed) {
+    return genForScopedSlot(key, el)
+  }
+  return `{key:${key},fn:function(${String(el.attrsMap.scope)}){` +
     `return ${el.tag === 'template'
       ? genChildren(el) || 'void 0'
       : genElement(el)
-  }}]`
+  }}}`
+}
+
+function genForScopedSlot (key: string, el: any) {
+  const exp = el.for
+  const alias = el.alias
+  const iterator1 = el.iterator1 ? `,${el.iterator1}` : ''
+  const iterator2 = el.iterator2 ? `,${el.iterator2}` : ''
+  el.forProcessed = true // avoid recursion
+  return `_l((${exp}),` +
+    `function(${alias}${iterator1}${iterator2}){` +
+      `return ${genScopedSlot(key, el)}` +
+    '})'
 }
 
 function genChildren (el: ASTElement, checkSkip?: boolean): string | void {

+ 8 - 3
src/core/instance/render-helpers/resolve-slots.js

@@ -42,11 +42,16 @@ function isWhitespace (node: VNode): boolean {
 }
 
 export function resolveScopedSlots (
-  fns: Array<[string, Function]>
+  fns: ScopedSlotsData, // see flow/vnode
+  res?: Object
 ): { [key: string]: Function } {
-  const res = {}
+  res = res || {}
   for (let i = 0; i < fns.length; i++) {
-    res[fns[i][0]] = fns[i][1]
+    if (Array.isArray(fns[i])) {
+      resolveScopedSlots(fns[i], res)
+    } else {
+      res[fns[i].key] = fns[i].fn
+    }
   }
   return res
 }

+ 31 - 0
test/unit/features/component/component-scoped-slot.spec.js

@@ -382,4 +382,35 @@ describe('Component scoped slot', () => {
     }).$mount()
     expect(vm.$el.innerHTML).toBe('<span>hello</span>')
   })
+
+  // #5615
+  it('scoped slot with v-for', done => {
+    const vm = new Vue({
+      data: { names: ['foo', 'bar'] },
+      template: `
+        <test ref="test">
+          <template v-for="n in names" :slot="n" scope="props">
+            <span>{{ props.msg }}</span>
+          </template>
+        </test>
+      `,
+      components: {
+        test: {
+          data: () => ({ msg: 'hello' }),
+          template: `
+            <div>
+              <slot name="foo" :msg="msg + ' foo'"></slot>
+              <slot name="bar" :msg="msg + ' bar'"></slot>
+            </div>
+          `
+        }
+      }
+    }).$mount()
+
+    expect(vm.$el.innerHTML).toBe('<span>hello foo</span> <span>hello bar</span>')
+    vm.$refs.test.msg = 'world'
+    waitForUpdate(() => {
+      expect(vm.$el.innerHTML).toBe('<span>world foo</span> <span>world bar</span>')
+    }).then(done)
+  })
 })