有条件地渲染父元素,保持内部html

时间:2017-04-08 11:20:28

标签: vue.js vuejs2

是否有任何内置方式可以有条件地显示父元素?

举例说明:

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

6 个答案:

答案 0 :(得分:7)

我认为这是自定义指令的工作。我把这个作为一个快速的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>

答案 1 :(得分:2)

我遇到了同样的问题。

Vue.js核心团队成员LinusBorg使用具有自定义渲染功能的功能组件为此用例提供了一个很好的解决方案:

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>

他的小提琴:https://jsfiddle.net/Linusborg/w9d8ujn8/

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

答案 2 :(得分:2)

如果有人碰巧正在使用 vue-fragment https://www.npmjs.com/package/vue-fragment)库,则可以执行以下操作:

<component :is="someCondition ? 'a' : 'fragment'">
   <span>This is always rendered no matter what</span>
</component>

话虽这么说,我不建议仅使用一个库来做到这一点。但是,如果您已经这样做了,那么它将很有用。

答案 3 :(得分:1)

没有Vuejs或其他框架上下文,只有DOM。 如果不删除子元素,也无法删除它。 你可以做的是获取DOM元素的子元素,并用父元素或类似的东西替换它们 使用Vuejs,您可以在指令或组件后面隐藏此功能,但我认为这会使您想要实现的目标过于复杂。

如果您希望锚点在某些情况下无法点击,您可以执行v-on:click.prevent="yourCondition && xxx()"之类的操作。最重要的是,您可以使用css类来隐藏它仍然是锚v-bind:class="{ fakeAnchor: yourCondition}"的事实。

虽然最简单的解决方案可能只是复制你的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>

最佳解决方案取决于您的情况。如果真正的内部内容会更大,那么重复它可能是不可取的。如果是这种情况,您可以将其封装在另一个vue组件中。

答案 4 :(得分:0)

也许这种方法可以帮助您(使用'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>

这样,父组件会根据“ someCondition”而变化,子组件在两种情况下都是相同的。

答案 5 :(得分:0)

对于 Vue v3.x,以下内容有效:

  <component
    :is="condition ? 'custom-component' : 'v-fragment'"
    custom-component-prop
    ...
  >
    ...
  </component>
// VFragment.vue
<template>
  <slot></slot>
</template>

<script>
  export default {
    inheritAttrs: false,
  }
</script>

对于 Vue v2.x,解决方法是:

  <component
    :is="condition ? 'custom-component' : 'v-div'"
    custom-component-prop
    ...
  >
    ...
  </component>
// VDiv.vue
<template>
  <div>
    <slot></slot>
  </div>
</template>

<script>
  export default {
    inheritAttrs: false,
  }
</script>

权衡是会有一个额外的元素,比如 div 被渲染,因为 Vue v2.x 不支持片段。