From 5a9da95b8a865416f082952a48416ffc091e4078 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 3 Nov 2017 16:45:54 -0400 Subject: [PATCH] fix(slots): properly handle nested named slot passing fix #6996 --- src/compiler/parser/index.js | 2 +- .../instance/render-helpers/render-slot.js | 12 +++++-- .../instance/render-helpers/resolve-slots.js | 11 +++--- .../features/component/component-slot.spec.js | 36 +++++++++++++++++++ 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/compiler/parser/index.js b/src/compiler/parser/index.js index 1f578103162..1a28e86de2e 100644 --- a/src/compiler/parser/index.js +++ b/src/compiler/parser/index.js @@ -467,7 +467,7 @@ function processSlot (el) { el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget // preserve slot as an attribute for native shadow DOM compat // only for non-scoped slots. - if (!el.slotScope) { + if (el.tag !== 'template' && !el.slotScope) { addAttr(el, 'slot', slotTarget) } } diff --git a/src/core/instance/render-helpers/render-slot.js b/src/core/instance/render-helpers/render-slot.js index a9ef129b729..a58daa74e62 100644 --- a/src/core/instance/render-helpers/render-slot.js +++ b/src/core/instance/render-helpers/render-slot.js @@ -12,6 +12,7 @@ export function renderSlot ( bindObject: ?Object ): ?Array { const scopedSlotFn = this.$scopedSlots[name] + let nodes if (scopedSlotFn) { // scoped slot props = props || {} if (bindObject) { @@ -23,7 +24,7 @@ export function renderSlot ( } props = extend(extend({}, bindObject), props) } - return scopedSlotFn(props) || fallback + nodes = scopedSlotFn(props) || fallback } else { const slotNodes = this.$slots[name] // warn duplicate slot usage @@ -37,6 +38,13 @@ export function renderSlot ( } slotNodes._rendered = true } - return slotNodes || fallback + nodes = slotNodes || fallback + } + + const target = props && props.slot + if (target) { + return this.$createElement('template', { slot: target }, nodes) + } else { + return nodes } } diff --git a/src/core/instance/render-helpers/resolve-slots.js b/src/core/instance/render-helpers/resolve-slots.js index 26ed51d742a..629177e4771 100644 --- a/src/core/instance/render-helpers/resolve-slots.js +++ b/src/core/instance/render-helpers/resolve-slots.js @@ -11,7 +11,6 @@ export function resolveSlots ( if (!children) { return slots } - const defaultSlot = [] for (let i = 0, l = children.length; i < l; i++) { const child = children[i] const data = child.data @@ -32,12 +31,14 @@ export function resolveSlots ( slot.push(child) } } else { - defaultSlot.push(child) + (slots.default || (slots.default = [])).push(child) } } - // ignore whitespace - if (!defaultSlot.every(isWhitespace)) { - slots.default = defaultSlot + // ignore slots that contains only whitespace + for (const name in slots) { + if (slots[name].every(isWhitespace)) { + delete slots[name] + } } return slots } diff --git a/test/unit/features/component/component-slot.spec.js b/test/unit/features/component/component-slot.spec.js index 4ceadfc2153..4e6deac39cb 100644 --- a/test/unit/features/component/component-slot.spec.js +++ b/test/unit/features/component/component-slot.spec.js @@ -743,4 +743,40 @@ describe('Component slot', () => { }).$mount() expect(vm.$el.children[0].getAttribute('slot')).toBe('foo') }) + + it('passing a slot down as named slot', () => { + const Bar = { + template: `
` + } + + const Foo = { + components: { Bar }, + template: `
` + } + + const vm = new Vue({ + components: { Foo }, + template: `
hello
` + }).$mount() + + expect(vm.$el.innerHTML).toBe('
hello
') + }) + + it('fallback content for named template slot', () => { + const Bar = { + template: `
fallback
` + } + + const Foo = { + components: { Bar }, + template: `
` + } + + const vm = new Vue({ + components: { Foo }, + template: `
` + }).$mount() + + expect(vm.$el.innerHTML).toBe('
fallback
') + }) })