1

Is there any way to do this kind of conditional nesting with Vue? (Apparently <component is="template"> outputs a non parsed <template> tag into the DOM but does not render anything)

<component :is="condition ? 'div' : 'template'">
    <!-- 2 elements here -->
</component>

The purpose is to avoid unneeded markup or repeating my 2 elements code twice in a v-if v-else.

Also having a sub component with the 2 elements would not help as Vue components need only 1 root, so a wrapper would be needed there too.

What I am looking for is an equivalent to:

<div v-if="condition">
    <span>element 1</span>
    <span>element 2</span>
</div>
<template v-else>
    <span>element 1</span>
    <span>element 2</span>
</template>

but without rewriting twice the span elements.

(Also posted it on Vue.js forum https://forum.vuejs.org/t/how-to-conditionally-nest-elements/95384)

Thanks for any help!

3
  • But even with component is you would be rendering two separate modules. Dynamic components right? So how does that reduce the additional mark up. It is just that they are in separate files? Commented May 16, 2020 at 6:32
  • @UtsavPatel, sorry I don't understand your question, maybe have a look at forum.vuejs.org/t/how-to-conditionally-nest-elements/95384 to have more details on what I want to do. :) Commented May 16, 2020 at 6:36
  • 1
    @antoni This post might help. Commented May 16, 2020 at 7:10

1 Answer 1

1

Using Vue 2:

There is no straight forward solution to this using Vue 2, but you can use Functional Components for this purpose, as functional components do not have the single-root limitation.

So first, create a my-span functional component which will be rendered in DOM with multiple nodes like:

<span>element 1</span>
<span>element 2</span>

using:

Vue.component('my-span', {
  functional: true,
  render: function (createElement, context) {
    const span1 = createElement('span', 'element 1');
    const span2 = createElement('span', 'element 2');
    return [span1, span2]
  },
})

You can create as many nodes you want, with any element you want and simply return that as an array.

In Vue 2.5.0+, if you are using single-file components, template-based functional components can be declared with:

<template functional>
</template>

Next, create a component just to wrap the <my-span> above like:

Vue.component('my-div', {
  template: '<div><my-span /></div>'
})

Then using Vue’s <component> element with the is special attribute, we can dynamically switch between the <my-div> and <my-span> components like:

<component :is="condition ? 'my-div' : 'my-span'"></component>

This will result in the desired behaviour you are looking for. You can also inspect the rendered DOM to verify this.

Working Demo:

Vue.component('my-span', {
  functional: true,
  render: function (createElement, context) {
    const span1 = createElement('span', 'element 1');
    const span2 = createElement('span', 'element 2');
    return [span1, span2]
  },
})

Vue.component('my-div', {
  template: '<div><my-span /></div>'
})

new Vue({
  el: "#myApp",
  data: {
    condition: true
  },
  methods: {
    toggle() {
      this.condition = !this.condition;
    }
  }
})
#myApp{padding:20px}
#myApp div{padding:10px;border:2px solid #eee}
#myApp span{padding:5px;margin:5px;display:inline-flex}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="myApp">
  <button @click="toggle">Toggle</button><br>
  <component :is="condition ? 'my-div' : 'my-span'"></component>
</div>


Using Vue 3:

In Vue 3, it would ve very easy to implement as we can have multiple root nodes in Vue 3, as you can see MySpan component has a template with multiple spans:

const MySpan = { template: '<span>element 1</span><span>element 2</span>' };

Working Demo:

const { createApp, ref } = Vue;
const MySpan = { template: '<span>element 1</span><span>element 2</span>' };
const MyDiv = { 
  components: { MySpan },
  template: '<div><my-span /></div>' 
};

const App = {
  components: { MyDiv, MySpan },
  setup() {
    const condition = ref(true);
    const toggle = () => {
      condition.value = !condition.value;
    };
    return { condition, toggle };
  }
};

createApp(App).mount("#myApp");
#myApp{padding:20px}
#myApp div{padding:10px;border:2px solid #eee}
#myApp span{padding:5px;margin:5px;display:inline-flex}
<script src="//unpkg.com/vue@next"></script>
<div id="myApp">
  <button @click="toggle">Toggle</button><br>
  <component :is="condition ? 'my-div' : 'my-span'"></component>
</div>

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your answer @palash. I am looking forward to using Vue 3.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.