Skip to content

Commit dae173d

Browse files
committedOct 4, 2017
feat: support denoting normal elements as scoped slot
also deprecate "scope" in favor of "slot-scope"
1 parent 4987eeb commit dae173d

File tree

5 files changed

+146
-20
lines changed

5 files changed

+146
-20
lines changed
 

Diff for: ‎src/compiler/codegen/index.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,8 @@ export function genData (el: ASTElement, state: CodegenState): string {
239239
data += `${genHandlers(el.nativeEvents, true, state.warn)},`
240240
}
241241
// slot target
242-
if (el.slotTarget) {
242+
// only for non-scoped slots
243+
if (el.slotTarget && !el.slotScope) {
243244
data += `slot:${el.slotTarget},`
244245
}
245246
// scoped slots
@@ -342,7 +343,7 @@ function genScopedSlot (
342343
if (el.for && !el.forProcessed) {
343344
return genForScopedSlot(key, el, state)
344345
}
345-
return `{key:${key},fn:function(${String(el.attrsMap.scope)}){` +
346+
return `{key:${key},fn:function(${String(el.slotScope)}){` +
346347
`return ${el.tag === 'template'
347348
? genChildren(el, state) || 'void 0'
348349
: genElement(el, state)

Diff for: ‎src/compiler/parser/index.js

+22-5
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const modifierRE = /\.[^.]+/g
3131
const decodeHTMLCached = cached(he.decode)
3232

3333
// configurable state
34-
export let warn
34+
export let warn: any
3535
let delimiters
3636
let transforms
3737
let preTransforms
@@ -428,14 +428,31 @@ function processSlot (el) {
428428
)
429429
}
430430
} else {
431+
let slotScope
432+
if (el.tag === 'template') {
433+
slotScope = getAndRemoveAttr(el, 'scope')
434+
/* istanbul ignore if */
435+
if (process.env.NODE_ENV !== 'production' && slotScope) {
436+
warn(
437+
`the "scope" attribute for scoped slots have been deprecated and ` +
438+
`replaced by "slot-scope" since 2.5. The new "slot-scope" attribute ` +
439+
`can also be used on plain elements in addition to <template> to ` +
440+
`denote scoped slots.`,
441+
true
442+
)
443+
}
444+
el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope')
445+
} else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) {
446+
el.slotScope = slotScope
447+
}
431448
const slotTarget = getBindingAttr(el, 'slot')
432449
if (slotTarget) {
433450
el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget
434451
// preserve slot as an attribute for native shadow DOM compat
435-
addAttr(el, 'slot', slotTarget)
436-
}
437-
if (el.tag === 'template') {
438-
el.slotScope = getAndRemoveAttr(el, 'scope')
452+
// only for non-scoped slots.
453+
if (!el.slotScope) {
454+
addAttr(el, 'slot', slotTarget)
455+
}
439456
}
440457
}
441458
}

Diff for: ‎src/shared/util.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export const isBuiltInTag = makeMap('slot,component', true)
106106
/**
107107
* Check if a attribute is a reserved attribute.
108108
*/
109-
export const isReservedAttribute = makeMap('key,ref,slot,is')
109+
export const isReservedAttribute = makeMap('key,ref,slot,slot-scope,is')
110110

111111
/**
112112
* Remove an item from an array

Diff for: ‎test/unit/features/component/component-scoped-slot.spec.js

+98-12
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ describe('Component scoped slot', () => {
55
const vm = new Vue({
66
template: `
77
<test ref="test">
8-
<template scope="props">
8+
<template slot-scope="props">
99
<span>{{ props.msg }}</span>
1010
</template>
1111
</test>
@@ -31,11 +31,39 @@ describe('Component scoped slot', () => {
3131
}).then(done)
3232
})
3333

34+
it('default slot (plain element)', done => {
35+
const vm = new Vue({
36+
template: `
37+
<test ref="test">
38+
<span slot-scope="props">{{ props.msg }}</span>
39+
</test>
40+
`,
41+
components: {
42+
test: {
43+
data () {
44+
return { msg: 'hello' }
45+
},
46+
template: `
47+
<div>
48+
<slot :msg="msg"></slot>
49+
</div>
50+
`
51+
}
52+
}
53+
}).$mount()
54+
55+
expect(vm.$el.innerHTML).toBe('<span>hello</span>')
56+
vm.$refs.test.msg = 'world'
57+
waitForUpdate(() => {
58+
expect(vm.$el.innerHTML).toBe('<span>world</span>')
59+
}).then(done)
60+
})
61+
3462
it('with v-bind', done => {
3563
const vm = new Vue({
3664
template: `
3765
<test ref="test">
38-
<template scope="props">
66+
<template slot-scope="props">
3967
<span>{{ props.msg }} {{ props.msg2 }} {{ props.msg3 }}</span>
4068
</template>
4169
</test>
@@ -65,11 +93,11 @@ describe('Component scoped slot', () => {
6593
}).then(done)
6694
})
6795

68-
it('template slot', done => {
96+
it('named scoped slot', done => {
6997
const vm = new Vue({
7098
template: `
7199
<test ref="test">
72-
<template slot="item" scope="props">
100+
<template slot="item" slot-scope="props">
73101
<span>{{ props.foo }}</span><span>{{ props.bar }}</span>
74102
</template>
75103
</test>
@@ -95,6 +123,34 @@ describe('Component scoped slot', () => {
95123
}).then(done)
96124
})
97125

126+
it('named scoped slot (plain element)', done => {
127+
const vm = new Vue({
128+
template: `
129+
<test ref="test">
130+
<span slot="item" slot-scope="props">{{ props.foo }} {{ props.bar }}</span>
131+
</test>
132+
`,
133+
components: {
134+
test: {
135+
data () {
136+
return { foo: 'FOO', bar: 'BAR' }
137+
},
138+
template: `
139+
<div>
140+
<slot name="item" :foo="foo" :bar="bar"></slot>
141+
</div>
142+
`
143+
}
144+
}
145+
}).$mount()
146+
147+
expect(vm.$el.innerHTML).toBe('<span>FOO BAR</span>')
148+
vm.$refs.test.foo = 'BAZ'
149+
waitForUpdate(() => {
150+
expect(vm.$el.innerHTML).toBe('<span>BAZ BAR</span>')
151+
}).then(done)
152+
})
153+
98154
it('fallback content', () => {
99155
const vm = new Vue({
100156
template: `<test></test>`,
@@ -120,7 +176,7 @@ describe('Component scoped slot', () => {
120176
const vm = new Vue({
121177
template: `
122178
<test ref="test">
123-
<template slot="item" scope="props">
179+
<template slot="item" slot-scope="props">
124180
<span>{{ props.text }}</span>
125181
</template>
126182
</test>
@@ -158,7 +214,7 @@ describe('Component scoped slot', () => {
158214
const vm = new Vue({
159215
template: `
160216
<test ref="test">
161-
<template slot="item" scope="props">
217+
<template slot="item" slot-scope="props">
162218
<span>{{ props.text }}</span>
163219
</template>
164220
</test>
@@ -221,7 +277,7 @@ describe('Component scoped slot', () => {
221277
const vm = new Vue({
222278
template: `
223279
<test ref="test">
224-
<template slot="item" scope="props">
280+
<template slot="item" slot-scope="props">
225281
<span>{{ props.text || 'meh' }}</span>
226282
</template>
227283
</test>
@@ -246,7 +302,7 @@ describe('Component scoped slot', () => {
246302
new Vue({
247303
template: `
248304
<test ref="test">
249-
<template slot="item" scope="props">
305+
<template slot="item" slot-scope="props">
250306
<span>{{ props.text }}</span>
251307
</template>
252308
</test>
@@ -343,8 +399,8 @@ describe('Component scoped slot', () => {
343399
},
344400
template: `
345401
<child>
346-
<template :slot="a" scope="props">A {{ props.msg }}</template>
347-
<template :slot="b" scope="props">B {{ props.msg }}</template>
402+
<template :slot="a" slot-scope="props">A {{ props.msg }}</template>
403+
<template :slot="b" slot-scope="props">B {{ props.msg }}</template>
348404
</child>
349405
`,
350406
components: { Child }
@@ -389,10 +445,10 @@ describe('Component scoped slot', () => {
389445
data: { names: ['foo', 'bar'] },
390446
template: `
391447
<test ref="test">
392-
<template v-for="n in names" :slot="n" scope="props">
448+
<template v-for="n in names" :slot="n" slot-scope="props">
393449
<span>{{ props.msg }}</span>
394450
</template>
395-
<template slot="abc" scope="props">
451+
<template slot="abc" slot-scope="props">
396452
<span>{{ props.msg }}</span>
397453
</template>
398454
</test>
@@ -417,4 +473,34 @@ describe('Component scoped slot', () => {
417473
expect(vm.$el.innerHTML).toBe('<span>world foo</span> <span>world bar</span> <span>world abc</span>')
418474
}).then(done)
419475
})
476+
477+
it('scoped slot with v-for (plain elements)', done => {
478+
const vm = new Vue({
479+
data: { names: ['foo', 'bar'] },
480+
template: `
481+
<test ref="test">
482+
<span v-for="n in names" :slot="n" slot-scope="props">{{ props.msg }}</span>
483+
<span slot="abc" slot-scope="props">{{ props.msg }}</span>
484+
</test>
485+
`,
486+
components: {
487+
test: {
488+
data: () => ({ msg: 'hello' }),
489+
template: `
490+
<div>
491+
<slot name="foo" :msg="msg + ' foo'"></slot>
492+
<slot name="bar" :msg="msg + ' bar'"></slot>
493+
<slot name="abc" :msg="msg + ' abc'"></slot>
494+
</div>
495+
`
496+
}
497+
}
498+
}).$mount()
499+
500+
expect(vm.$el.innerHTML).toBe('<span>hello foo</span> <span>hello bar</span> <span>hello abc</span>')
501+
vm.$refs.test.msg = 'world'
502+
waitForUpdate(() => {
503+
expect(vm.$el.innerHTML).toBe('<span>world foo</span> <span>world bar</span> <span>world abc</span>')
504+
}).then(done)
505+
})
420506
})

Diff for: ‎test/unit/modules/compiler/codegen.spec.js

+22
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,28 @@ describe('codegen', () => {
179179
)
180180
})
181181

182+
it('generate scoped slot', () => {
183+
assertCodegen(
184+
'<foo><template slot-scope="bar">{{ bar }}</template></foo>',
185+
`with(this){return _c('foo',{scopedSlots:_u([{key:"default",fn:function(bar){return [_v(_s(bar))]}}])})}`
186+
)
187+
assertCodegen(
188+
'<foo><div slot-scope="bar">{{ bar }}</div></foo>',
189+
`with(this){return _c('foo',{scopedSlots:_u([{key:"default",fn:function(bar){return _c('div',{},[_v(_s(bar))])}}])})}`
190+
)
191+
})
192+
193+
it('generate named scoped slot', () => {
194+
assertCodegen(
195+
'<foo><template slot="foo" slot-scope="bar">{{ bar }}</template></foo>',
196+
`with(this){return _c('foo',{scopedSlots:_u([{key:"foo",fn:function(bar){return [_v(_s(bar))]}}])})}`
197+
)
198+
assertCodegen(
199+
'<foo><div slot="foo" slot-scope="bar">{{ bar }}</div></foo>',
200+
`with(this){return _c('foo',{scopedSlots:_u([{key:"foo",fn:function(bar){return _c('div',{},[_v(_s(bar))])}}])})}`
201+
)
202+
})
203+
182204
it('generate class binding', () => {
183205
// static
184206
assertCodegen(

0 commit comments

Comments
 (0)
Please sign in to comment.