From 0ccefff794c5fe73b80b9faf5ae5bfd8ec1a500f Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 9 May 2017 23:00:15 +0800 Subject: [PATCH] support v-for on scoped slots (fix #5615) --- flow/component.js | 2 +- flow/vnode.js | 4 ++- src/compiler/codegen/index.js | 19 ++++++++++-- .../instance/render-helpers/resolve-slots.js | 11 +++++-- .../component/component-scoped-slot.spec.js | 31 +++++++++++++++++++ 5 files changed, 60 insertions(+), 7 deletions(-) diff --git a/flow/component.js b/flow/component.js index e079df4feb9..d23b27d3b91 100644 --- a/flow/component.js +++ b/flow/component.js @@ -106,7 +106,7 @@ declare interface Component { // check custom keyCode _k: (eventKeyCode: number, key: string, builtInAlias: number | Array | 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 diff --git a/flow/vnode.js b/flow/vnode.js index 90a5921b3d8..33b5ce1df1f 100644 --- a/flow/vnode.js +++ b/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>; diff --git a/src/compiler/codegen/index.js b/src/compiler/codegen/index.js index ee89356d6db..5f8d8cf99c3 100644 --- a/src/compiler/codegen/index.js +++ b/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 { diff --git a/src/core/instance/render-helpers/resolve-slots.js b/src/core/instance/render-helpers/resolve-slots.js index ea79640f168..6fe2803040c 100644 --- a/src/core/instance/render-helpers/resolve-slots.js +++ b/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 } diff --git a/test/unit/features/component/component-scoped-slot.spec.js b/test/unit/features/component/component-scoped-slot.spec.js index 4f076a805b9..f97556b20ed 100644 --- a/test/unit/features/component/component-scoped-slot.spec.js +++ b/test/unit/features/component/component-scoped-slot.spec.js @@ -382,4 +382,35 @@ describe('Component scoped slot', () => { }).$mount() expect(vm.$el.innerHTML).toBe('hello') }) + + // #5615 + it('scoped slot with v-for', done => { + const vm = new Vue({ + data: { names: ['foo', 'bar'] }, + template: ` + + + + `, + components: { + test: { + data: () => ({ msg: 'hello' }), + template: ` +
+ + +
+ ` + } + } + }).$mount() + + expect(vm.$el.innerHTML).toBe('hello foo hello bar') + vm.$refs.test.msg = 'world' + waitForUpdate(() => { + expect(vm.$el.innerHTML).toBe('world foo world bar') + }).then(done) + }) })