Conditionally rendering parent element, keep inner

2020-03-01 18:55发布

问题:

Is there any built-in way to go about conditionally showing a parent element?

To illustrate:

<a v-show-but-keep-inner="someCondition">
    <span>This is always rendered no matter what</span>
</a

回答1:

I think it's a job for custom directive. I made this one as a quick POC:

Vue.directive('showButKeepInner', {
  bind(el, bindings) {
    bindings.def.wrap = function(el) {
      // Find all next siblings with data-moved and move back into el
      while (el.nextElementSibling && el.nextElementSibling.dataset.moved) {
        el.appendChild(el.nextElementSibling).removeAttribute('data-moved')
      }
      el.hidden = false
    }

    bindings.def.unwrap = function(el) {
      // Move all children of el outside and mark them with data-moved attr
      Array.from(el.children).forEach(child => {
        el.insertAdjacentElement('afterend', child).setAttribute('data-moved', true)
      })
      el.hidden = true
    }
  },

  inserted(el, bindings) {
    bindings.def[bindings.value ? 'wrap' : 'unwrap'](el)
  },

  update(el, bindings) {
    bindings.def[bindings.value ? 'wrap' : 'unwrap'](el)
  }
})

new Vue({
  el: '#app',
  data: {
    someCondition: false
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.min.js"></script>

<div id="app">
  <p>
    <button v-on:click="someCondition = !someCondition">{{ someCondition }}</button>
  </p>
  <a v-show-but-keep-inner="someCondition" href="/">
    <span>This is always rendered no matter what</span>
  </a>
</div>



回答2:

I just ran into the same Problem.

Vue.js Core team member LinusBorg provides a great solution for this use case using a functional component with a custom render function:

Vue.component('with-root', {
  functional: true,
  props: ['show'],
  render(h, ctx) {
    const children = ctx.children.filter(vnode => vnode.tag) // remove unnecessary text nodes
    console.log(children)
    if (children.length !== 1) {
      console.warn('this component accepts only one root node in its slot')
    }
    if (ctx.props.show) {
      return children[0]
    } else {
      return children[0].children
    }
  }
})

new Vue({
  el: '#app',
  data: {
    show: true
  }
})
<script src="https://unpkg.com/vue/dist/vue.js"></script>

<div id="app">
  <with-root v-bind:show="show">
    <a href="#">
      <span>This is always rendered no matter what</span>
    </a>
  </with-root>
  <br>
  <button @click="show = !show">Toggle</button>
  <pre>{{$data}}</pre>
</div>

His fiddle: https://jsfiddle.net/Linusborg/w9d8ujn8/

Source: https://forum.vuejs.org/t/conditionally-render-parent-element/9324/2



回答3:

Without Vuejs or other framework context, solely DOM. You can't remove a DOM element without removing its children too. What you could do is get the children of a DOM element and replace them with the parent or something similar like that.
With Vuejs you may be able to hide this functionality behind a directive or component, but I think this would be overcomplicating what you want to achieve.

If you want your anchor not to be clickable in certain cases, you could do something like v-on:click.prevent="yourCondition && xxx()". On top of that you could use css classes to hide the fact that it's still an anchor v-bind:class="{ fakeAnchor: yourCondition}".

Though the simplest solution may be to just duplicate your html.

<a v-show="someCondition">
    <span>This is always rendered no matter what</span>
</a>
<span v-show="!someCondition">This is always rendered no matter what</span>

The best solution depends on what your case is. If the real inner content will be much larger it might not be okay to duplicate that. If that's the case you could encapsulate that in another vue component.



回答4:

Maybe this approach helps you (using 'is'):

<template lang="pug">
  component(is=someCondition?"v-show-but-keep-inner":"my-another-component")
    v-form
      v-layout
        v-flex
          v-btn(@click="doThat")
</template>

This way parent component changes depending on 'someCondition' and children are the same for both conditions.



标签: vue.js vuejs2