Skip to content

Commit 3446d14

Browse files
committed
support functional components as named slot (fix #3872)
1 parent 5925ad3 commit 3446d14

File tree

4 files changed

+45
-3
lines changed

4 files changed

+45
-3
lines changed

src/core/instance/render.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ export function resolveSlots (
232232
child = children[i]
233233
// named slots should only be respected if the vnode was rendered in the
234234
// same context.
235-
if (child.context === context &&
235+
if ((child.context === context || child.functionalContext === context) &&
236236
child.data && (name = child.data.slot)) {
237237
const slot = (slots[name] || (slots[name] = []))
238238
if (child.tag === 'template') {

src/core/vdom/create-component.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ function createFunctionalComponent (
100100
props[key] = validateProp(key, propOptions, propsData)
101101
}
102102
}
103-
return Ctor.options.render.call(
103+
const vnode = Ctor.options.render.call(
104104
null,
105105
// ensure the createElement function in functional components
106106
// gets a unique context - this is necessary for correct named slot check
@@ -113,6 +113,11 @@ function createFunctionalComponent (
113113
slots: () => resolveSlots(children, context)
114114
}
115115
)
116+
vnode.functionalContext = context
117+
if (data.slot) {
118+
(vnode.data || (vnode.data = {})).slot = data.slot
119+
}
120+
return vnode
116121
}
117122

118123
export function createComponentInstanceForVnode (

src/core/vdom/vnode.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export default class VNode {
88
elm: Node | void;
99
ns: string | void;
1010
context: Component | void; // rendered in this component's scope
11+
functionalContext: Component | void; // only for functional component root nodes
1112
key: string | number | void;
1213
componentOptions: VNodeComponentOptions | void;
1314
child: Component | void; // component instance
@@ -35,6 +36,7 @@ export default class VNode {
3536
this.elm = elm
3637
this.ns = ns
3738
this.context = context
39+
this.functionalContext = undefined
3840
this.key = data && data.key
3941
this.componentOptions = componentOptions
4042
this.child = undefined

test/unit/features/component/component-slot.spec.js

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe('Component slot', () => {
2929
expect(child.$el.childNodes.length).toBe(0)
3030
})
3131

32-
it('default content', done => {
32+
it('default slot', done => {
3333
mount({
3434
childTemplate: '<div><slot></slot></div>',
3535
parentContent: '<p>{{ msg }}</p>'
@@ -43,6 +43,20 @@ describe('Component slot', () => {
4343
}).then(done)
4444
})
4545

46+
it('named slot', done => {
47+
mount({
48+
childTemplate: '<div><slot name="test"></slot></div>',
49+
parentContent: '<p slot="test">{{ msg }}</p>'
50+
})
51+
expect(child.$el.tagName).toBe('DIV')
52+
expect(child.$el.children[0].tagName).toBe('P')
53+
expect(child.$el.children[0].textContent).toBe('parent message')
54+
vm.msg = 'changed'
55+
waitForUpdate(() => {
56+
expect(child.$el.children[0].textContent).toBe('changed')
57+
}).then(done)
58+
})
59+
4660
it('fallback content', () => {
4761
mount({
4862
childTemplate: '<div><slot><p>{{msg}}</p></slot></div>'
@@ -536,4 +550,25 @@ describe('Component slot', () => {
536550
vm.$mount()
537551
expect('Error when rendering root').not.toHaveBeenWarned()
538552
})
553+
554+
// #3872
555+
it('functional component as slot', () => {
556+
const vm = new Vue({
557+
template: `
558+
<parent>
559+
<child>one</child>
560+
<child slot="a">two</child>
561+
</parent>
562+
`,
563+
components: {
564+
parent: {
565+
template: `<div><slot name="a"></slot><slot></slot></div>`
566+
},
567+
child: {
568+
template: '<div><slot></slot></div>'
569+
}
570+
}
571+
}).$mount()
572+
expect(vm.$el.innerHTML.trim()).toBe('<div>two</div><div>one</div>')
573+
})
539574
})

0 commit comments

Comments
 (0)