Reputation: 1174
I have this huge annoying component that needs to be repeated many times in the parent template because the parent template is using v-if. Here is the component code:
<SelectCard
v-for="(channel, index) in category.visibleChannels"
:key="index + '-' + channel.id"
:channel="channel"
:channel-selected="isSelected(channel.id)"
:read-more-details="channelInfoDetails"
@select="onAddChannel"
@deselect="onRemoveChannel"
@read-more-changed="setChannelInfoDetails"
/>
The only thing that changes between each time I render the template is what array I loop over.... Here is a simplified version of the problem:
<template>
<div
ref="channels"
class="channels"
>
<div v-if="showCategories">
<div
v-for="category in sliderCategories"
:key="category.name"
>
<h3 v-text="category.name" />
<div
v-if="category.showAll"
class="channel-list show-all"
:class="channelListSize"
>
<ul>
<SelectCard looping over category.contents />
</ul>
</div>
<ChannelSlider
v-else
:category="category"
@visible-updated="setVisibleChannels"
>
<SelectCard looping over category.visibleChannels />
</ChannelSlider>
<div class="show-all-link">
<a
:class="category.showAll?'arrow-up':'arrow-down'"
class="link"
@keyup.enter="toggleShowAll(category.name, !category.showAll)"
@click="toggleShowAll(category.name, !category.showAll)"
v-text="showAllText(category.showAll)"
/>
</div>
</div>
</div>
<div v-else>
<div v-if="showNoSearchResult">
<SomeComponent with some props/>
</div>
<div :class="channelListSize" class="channel-list">
<ul>
<SelectCard looping over updatedChannels />
</ul>
</div>
</div>
<div
ref="someref"
class="someClass"
:style="{top: channelInfoDetails.top + 'px', position: 'absolute'}"
>
<AnotherComponent with some props/>
</div>
</div>
</template>
So my template becomes HUGE because the SelectCard code has so many props.
Is there a way I can put SelectCard in a method in the parent code, so that I can just call a function with the array to use or something? Or is there another solution that I don't know of?
Upvotes: 0
Views: 1328
Reputation: 29092
I don't think there's a solution here that is as simple as you might like. But there are some possibilities.
You can reduce it down slightly by using the object form of v-bind
and v-on
. For the v-bind
you'd need to introduce a method to return the object as your props depend on channel
and index
, so they would need passing to the method. This would cut it down a bit but it isn't great. The object form of the is
attribute might also be an option. That might squeeze it down a little further but at the expense of clarity.
Another approach would be to introduce another component and then use slots for the SelectCard
. e.g.:
<div>
<div v-if="conditionA">
<div v-if="conditionA-A">
<slot />
</div>
<div v-else>
<slot />
</div>
</div>
<div v-else>
<div v-if="conditionB-A">
<slot />
</div>
<div v-else>
<slot />
</div>
</div>
</div>
You'd then pass the SelectCard
in as the slot contents, with a computed property to make the array dynamic.
One problem with this approach is that you may find yourself having to pass a lot of stuff around between the various layers of component to get it working.
A further option is to convert everything to a render
function. You definitely can do what you're trying to do using a render
function but that would come at the expense of having to forgo using a template. Whether that is really a problem would depend on the complexity of the rest of the template.
Upvotes: 2
Reputation: 5048
Put all the logic of the v-if's
into a computed property that returns the correct array that you want to pass as . props to the SelectCard
something like:
<SelectCard :arr="arrayToRender"/>
...
computed: {
arrayToRender(){
if (ConditionA){ return Array_A}
if ....
}
Upvotes: 0