Skip to content

Commit

Permalink
support v-for on scoped slots (fix #5615)
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed May 9, 2017
1 parent dc00590 commit 0ccefff
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 7 deletions.
2 changes: 1 addition & 1 deletion flow/component.js
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion flow/vnode.js
Expand Up @@ -71,4 +71,6 @@ declare type VNodeDirective = {
arg?: string;
modifiers?: ASTModifiers;
def?: Object;
}
};

declare type ScopedSlotsData = Array<{ key: string, fn: Function } | ScopedSlotsData>;
19 changes: 17 additions & 2 deletions src/compiler/codegen/index.js
Expand Up @@ -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 {
Expand Down
11 changes: 8 additions & 3 deletions src/core/instance/render-helpers/resolve-slots.js
Expand Up @@ -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 changes: 31 additions & 0 deletions test/unit/features/component/component-scoped-slot.spec.js
Expand Up @@ -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>

This comment has been minimized.

Copy link
@defcc

defcc May 9, 2017

Member

Maybe we could improve the test case to check v-for scoped slot and single scoped slot used together ? I'll open a PR to update it.

This comment has been minimized.

Copy link
@yyx990803

yyx990803 May 9, 2017

Author Member

👍

</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)
})
})

0 comments on commit 0ccefff

Please sign in to comment.