自定义Vue指令,以省略标记但呈现标记的内容?

时间:2017-09-13 15:43:40

标签: vue.js vuejs2

我想创建一个自定义Vue指令来省略标记,但在指令为真时呈现标记的内容。

例如,如果我的vue实例的数据被定义为

 data:{
    omitIt: true
 }

如果标记看起来像这样:

 <div v-omit="omitIt" class="someClass">
      Hello world!
 </div>

omitIt设置为false时,我希望将以下内容渲染到dom中:

<div class="someClass">
      Hello world!
 </div>

但是当omitIt为真时,我只想将以下内容渲染到dom中:

Hello world!

我最初尝试通过执行以下操作来解决此问题(诚然,这不是自定义vue指令):

 <template v-if="!omitIt">
      <div class="someClass">
  </template>
  Hello world!
  <template v-if="!omitIt">
      </div>
  </template>

以上并不漂亮,但我想也许它会起作用。但是,当omitIt为假时,会被渲染到dom中的是:

 <div class="someClass"></div>
 Hello world!

有关如何实现我正在寻找的结果的任何建议?

2 个答案:

答案 0 :(得分:3)

这个答案错误,插槽不能以这种方式使用。请改为Bert's answer

最简单的解决方案是为此目的创建一个包含slots的包装器组件,将省略的参数作为prop传递。
内容分发部分变得相当简单。

在包装器组件模板中:

<slot v-if="omitIt"></slot>
<div v-else>
  <slot></slot>
</div>

您想要使用包装器的任何地方:

<wrapper v-bind:omitIt="omitIt">
  // Content
</wrapper>

答案 1 :(得分:3)

我认为@Nit的答案是一个非常简单的答案并且支持它,但它确实有一个缺陷:插槽可能不是根元素,因此当需要省略包装时组件将失败。这是因为插槽可以包含多个元素,如果插槽 包含多个元素,则最终可能会有多个根元素,这是不允许的。

我有部分解决方案,如果组件换行,则只会在插槽中呈现第一个元素。

Vue.component("wrapper", {
  props:{
    nowrap: {type: Boolean, default: false}
  },
  render(h){
    // This will *only* render the *first* element contained in 
    // the default slot if `nowrap` is set. This is because a component
    // *must* have a single root element
    if (this.nowrap) return this.$slots.default[0]
    // Otherwise, wrap the contents in a DIV and render the contents
    return h('div', this.$slots.default)
  }
})

这是一个工作的例子。

console.clear()

Vue.component("wrapper", {
  props:{
    nowrap: {type: Boolean, default: false}
  },
  render(h){
    // Log a warning if content is being omitted
    const omissionMessage = "Wrapper component contains more than one root node with nowrap specified. Only the first node will be rendered."
    if (this.$slots.default.length > 1 && this.nowrap)
      console.warn(omissionMessage)
    
    // This will *only* render the *first* element contained in 
    // the default slot if `nowrap` is set. This is because a component
    // *must* have a single root element
    if (this.nowrap) return this.$slots.default[0]
    
    // Otherwise, wrap the contents in a DIV and render the contents
    return h('div', this.$slots.default)
  }
})

new Vue({
  el: "#app"
})
.someClass{
  color: blue
}
<script src="https://unpkg.com/vue@2.4.2"></script>
<div id="app">
  <wrapper class="someClass">Hello World</wrapper>
  <wrapper nowrap>No wrap, single root</wrapper> <br>
  <wrapper nowrap>
    No wrap, two roots. Paragraph is ommitted.
    <p>Some other content</p>
  </wrapper>
</div>

一些注意事项:除非您将nowrap添加为属性,否则组件将始终换行。另外,请注意,类被添加到包装容器中,而不将其指定为prop。这是因为Vue会自动渲染未在组件的根元素上指定为props的属性,除非您告诉它不要。