Is it possible to use Vue's <component>
to conditionally render a component or nothing instead?
For instance, consider the following pattern:
<component :is="hasTooltip ? 'CustomTooltip' : 'div'">
<div>Hover over me!</div>
</component>
In this case, we are rendering a CustomTooltip
component when the hasTooltip
value is true, otherwise we are rendering a normal div. Is it possible to render a fragment in place of this div( like the <>
fragment in React) that will get stripped out in the browser? How might this be accomplished? I'm on Vue 2.0.
Conditionally rendering the component won't work because we want the "Hover over me!" text to render regardless.
CodePudding user response:
Something like this maybe ?
Vue.component('tooltip', {
template: `<div> Tooltip for: <slot></slot> </div>`
})
Vue.component('wrap-if', {
functional: true,
props: ['wrap', 'wrapper'],
render(h, ctx) {
if (ctx.props.wrap) {
return h(ctx.props.wrapper, ctx.data, ctx.children)
} else {
// maybe check that children.length === 1 as Vue 2 does not support fragments
return ctx.children
}
}
})
new Vue({
el: '#app',
data: {
wrap: true
}
})
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<div id="app">
<wrap-if :wrap="wrap" wrapper="tooltip">
<span>This is always rendered no matter what</span>
</wrap-if>
<br>
<button @click="wrap = !wrap">Toggle</button>
<pre>{{$data}}</pre>
</div>
CodePudding user response:
Just consolidating some other comments on this into an answer.
It appears that there's no clean way of conditionally wrapping a component without a plugin in Vue-2, because fragments are not available.
However, this same pattern can be accomplished with either a Vue-2 fragment plugin, or in Vue3, with the use of the fragment
component, as in this answer. Instead of rendering the div, render the fragment.
CodePudding user response:
You can achieve conditional wrapper around an element with v-if/v-else
, as outlined below, but you are duplicating the content to be wrapped, not referencing the same exact instance.
Obviously, if it's too big and it's exactly the same you can always define it as a separate Vue component.
<CustomTooltip v-if="hasTooltip">
<div v-text="hoverText" />
</CustomTooltip>
<div v-else v-text="hoverText" />
If it has to be the same instance, I would recommend using portal-vue package, which allows porting current contents to any other place in DOM.
<CustomTooltip v-if="hasTooltip">
<portal-target name="destination" />
</CustomTooltip>
<portal to="destination">
<div>Hover over me!</div>
</portal>