Reputation: 461
I'm trying to build a custom component from a render function.
This component being rendered accepts any number of slots. In the example below there are three available slots (named element_1
, element_2
, element_3
).
The below Array.reduce()
is meant to be equivalent to:
scopedSlots: {
"element_1": () => createElement('div', 'hello world'),
"element_2": () => createElement('div', 'hello world'),
"element_3": () => createElement('div', 'hello world'),
}
This is a slimmed down example with Array.reduce()
:
const records = [
{
"index": 1,
},
{
"index": 2,
},
{
"index": 3,
}
]
render: function (createElement) {
return createElement("replicator-component", {
attrs: { elements: records.length},
scopedSlots: records.reduce((a,x) => ({...a,
['element_' + x.index]:
() => { createElement( 'div', 'hello world') }}), {})
});
},
However nothing renders and there's no errors to guide me. Any ideas?
Upvotes: 1
Views: 2347
Reputation: 1
That reduce method doesn't work because it's missing a return before createElement('div', 'hello world')
:
Full example
const ReplicatorComponent = {
template: `
<div>
<h1>replicator-component</h1>
<slot name='element_1'></slot>
<slot name='element_2'></slot>
<slot name='element_3'></slot>
</div>
`
}
const records = [{
"index": 1,
},
{
"index": 2,
},
{
"index": 3,
}
]
Vue.component('my-component', {
render: function(createElement) {
let slotContent = records.reduce((a, x) => ({ ...a,
['element_' + x.index]:
() => {
return createElement('div', 'hello world')
}
}), {})
return createElement(ReplicatorComponent, {
attrs: {
elements: records.length
},
scopedSlots: slotContent
});
},
})
var app = new Vue({
el: '#app',
data: () => ({})
})
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<div id="app">
test
<my-component></my-component>
</div>
Upvotes: 0
Reputation: 665380
The difference is that in your reduce
, you are creating the functions as
() => { createElement( 'div', 'hello world') }
while in your hardcoded version (and also in the forEach
loop in @Boussadjra's anwer) they are created as
() => createElement('div', 'hello world')
which actually return
the created element. It's nothing to do with the use of reduce
, which is fine.
const ReplicatorComponent = {
template: `<div>
<h1>replicator-component</h1>
<slot name='element_1'></slot>
<slot name='element_2'></slot>
<slot name='element_3'></slot>
</div>`
};
const records = [
{ "index": 1 },
{ "index": 2 },
{ "index": 3 },
];
Vue.component('my-component', {
render: function(createElement) {
return createElement(ReplicatorComponent, {
attrs: {
elements: records.length
},
scopedSlots: records.reduce((a,x) => ({
...a,
['element_' + x.index]: () =>
createElement( 'div', 'hello world')
}), {})
});
},
});
new Vue({
el: '#app',
data: () => ({})
});
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<div id="app">
<my-component></my-component>
</div>
Upvotes: 2
Reputation: 111
i think scoped slots should be functions that take props
as their parameter
according to the Vue.js docs
export default {
render(createElement) {
// ...
// some other stuff
// ...
// Scoped slots in the form of
// { name: props => VNode | Array<VNode> }
scopedSlots: {
default: props => createElement('span', props.text)
},
}
}
so maybe you should try that
also you can accomplish the same thing using vue's new unified v-slot
system
<!-- page component -->
<template>
<some-component>
<template v-for="slot in scopedSlots" v-slot:[slot]="props">
hello {{props}}
</template>
</some-component>
</template>
<!-- some-component.vue -->
<template>
<div>
<slot v-for="slot in Object.keys($slots)" :name="slot"></slot>
</div>
</template>
Upvotes: 0