仅在显示内容时显示插槽

Ste*_*man 25 vue.js vuejs2

有没有办法只显示一个插槽,如果它有任何内容?

例如,我正在构建一个简单的Card.vue组件,如果页脚槽有内容,我只想显示页脚:

模板:

<template>

    <div class="panel" :class="panelType">

        <div class="panel-heading">
            <h3 class="panel-title">
                <slot name="title">
                    Default Title
                </slot>
            </h3>
        </div>

        <div class="panel-body">
            <slot name="body"></slot>

            <p class="category">
                <slot name="category"></slot>
            </p>
        </div>

        <div class="panel-footer" v-if="hasFooterSlot">
            <slot name="footer"></slot>
        </div>

    </div>

</template>
Run Code Online (Sandbox Code Playgroud)

脚本:

<script>
    export default {
        props: {
            active: true,
            type: {
                type: String,
                default: 'default',
            },
        },

        computed: {
            panelType() {
                return `panel-${this.type}`;
            },

            hasFooterSlot() {
                return this.$slots['footer']
            }
        }
    }
</script>
Run Code Online (Sandbox Code Playgroud)

在视图中:

<card type="success"></card>
Run Code Online (Sandbox Code Playgroud)

由于上面的组件不包含页脚,因此不应该呈现,但它是.

我尝试过使用this.$slots['footer'],但这会返回undefined.

有人有任何提示吗?

小智 54

现在,在 Vue3 组合 API 中,您可以使用useSlots.

<script setup>
import { useSlots } from 'vue'
const slots = useSlots()
</script>

<template>
  <div v-if="slots.content" class="classname">
    <slot name="content"></slot>
  </div>
</template>
Run Code Online (Sandbox Code Playgroud)

  • 只是缺少一个“import { useSlots } from 'vue'”,但其他方面都很好。 (4认同)

Ber*_*ert 42

它应该在

this.$slots.footer
Run Code Online (Sandbox Code Playgroud)

所以,这应该工作.

hasFooterSlot() {
  return !!this.$slots.footer
}
Run Code Online (Sandbox Code Playgroud)

例子.

  • 请注意,在 Vue 3 中,$slots 中的属性现在是方法,因此您需要使用如下语法:`this.$slots.footer()` (7认同)
  • 这似乎并不适用于 Vue 2.6 中的所有情况 - 当存在其他 $scopedSlots 时,错误地返回 false(至少在某些情况下,例如从 dom 中删除并通过“v-if”添加回来的元素) ) (4认同)
  • “ this。$ slots.footer”和“ this。$ slots ['footer']”是等效的,不是吗?这真的是解决方案,还是还有其他问题? (2认同)
  • @DanielBeck你是对的,他们都指向同一件事。如果没有内容,两者都会返回 undefined。唯一不同的是我返回一个布尔值,但是,测试它,从 hasFooterSlot 返回 undefined 将导致 v-if 隐藏组件。所以,从技术上讲,我认为你的代码应该可以工作。 (2认同)
  • @launchoverit 不太有效,因为如果未使用插槽,则不会定义 `this.$slots.footer()` ,并且您会收到异常。在 vue 3 中调用它之前,你必须检查 `this.$slots.footer` 是否存在(这在文档/迁移指南中相当不清楚,使得我们必须重构所有内容两次..:/ (2认同)

biq*_*llo 22

CSS 大大简化了这个过程。只需使用以下代码即可!

.panel-footer:empty {
  display: none;
}
Run Code Online (Sandbox Code Playgroud)


Mus*_*aya 13

这是 Vue 3 组合 API 的解决方案:

<template>
    <div class="md:grid md:grid-cols-5 md:gap-6">

        <!-- Here, you hide the wrapper if there is no used slot or empty --> 
        <div class="md:col-span-2" v-if="hasTitle">
            <slot name="title"></slot>
        </div>

        <div class="mt-5 md:mt-0" 
            :class="{'md:col-span-3': hasTitle, 'md:col-span-5': !hasTitle}">
            <div class="bg-white rounded-md shadow">
                <div class="py-7">
                    <slot></slot>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
import {ref} from "vue";

export default {
    setup(props, {slots}) {
        const hasTitle = ref(false)

        // Check if the slot exists by name and has content.
        // It returns an empty array if it's empty.
        if (slots.title && slots.title().length) {
            hasTitle.value = true
        }

        return {
            hasTitle
        }
    }
}
</script>
Run Code Online (Sandbox Code Playgroud)


Mad*_*adi 9

您应该检查vm.$slots,也vm.$scopedSlots为它。

hasSlot (name = 'default') {
   return !!this.$slots[ name ] || !!this.$scopedSlots[ name ];
}
Run Code Online (Sandbox Code Playgroud)

  • 我的`this。$ slots`是一个空对象,`this。$ scopedSlots`可以帮助我。 (3认同)

Sye*_*yed 6

简而言之,在内联中执行此操作:

<template lang="pug">
  div
    h2(v-if="$slots.title")
      slot(name="title")
    h3(v-if="$slots['sub-title']")
      slot(name="sub-title")
</template>
Run Code Online (Sandbox Code Playgroud)


Fra*_*igh 5

我遇到了一个类似的问题,但是在广泛的代码库中,并且在创建原子设计结构化组件时,编写hasSlot()方法一直很累人,而且当涉及到 TDD 时 - 它是另一种测试方法......说,你可以总是将原始逻辑放在 a 中,v-if但我发现模板最终会变得混乱且难以阅读,尤其是对于检查代码结构的新开发人员而言。

div当没有提供插槽时,我的任务是找出一种删除插槽父级的方法。

问题:

<template>
  <div>
    <div class="hello">
      <slot name="foo" />
    </div>
    <div class="world">
      <slot name="bar" />
    </div>
  </div>
</template>

//instantiation
<my-component>
  <span slot="foo">show me</span>
</my-component>

//renders
<div>
  <div class="hello">
    <span slot="foo">show me</span>
  </div>
  <div class="world"></div>
</div>
Run Code Online (Sandbox Code Playgroud)

如您所见,问题是我有一个几乎“尾随”的 div,当组件作者决定不需要bar插槽时,它可能会提供样式问题。

ofcourse我们可以去<div v-if="$slots.bar">...</div><div v-if="hasBar()">...</div>等,但就像我说的-即可以得到烦人,最终落得难以阅读。

解决方案

我的解决方案是制作一个通用slot组件,它只是渲染出一个带有周围 div 的插槽......见下文。

//slot component
<template>
  <div v-if="!!$slots.default">
    <slot />
  </div>
</template>


//usage within <my-component/>
<template>
  <div>
    <slot-component class="hello">
      <slot name="foo"/>
    </slot-component>
    <slot-component class="world">
      <slot name="bar"/>
    </slot-component>
  </div>
</template>

//instantiation
<my-component>
  <span slot="foo">show me</span>
</my-component>

//renders
<div>
  <div class="hello">
    <span>show me</span>
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)

我在尝试这个想法时遇到了用例问题,有时我的标记结构需要为了这种方法的好处而改变。这种方法减少了在每个组件模板中进行小槽检查的需要。我想您可以将组件视为<conditional-div />组件...

还值得注意的是,将属性应用于slot-component实例化 ( <slot-component class="myClass" data-random="randomshjhsa" />) 是可以的,因为属性会逐渐渗入slot-component模板的包含 div 中。

希望这可以帮助。

更新 我为此编写了一个插件,因此不再需要custom-slot在每个消费者组件中导入组件,您只需在 main.js 实例化中编写 Vue.use(SlotPlugin) 即可。(见下文)

const SLOT_COMPONENT = {
  name: 'custom-slot',
  template: `
    <div v-if="$slots.default">
      <slot />
    </div>
  `
}

const SLOT_PLUGIN = {
  install (Vue) {
    Vue.component(SLOT_COMPONENT.name, SLOT_COMPONENT)
  }
}

export default SLOT_PLUGIN

//main.js
import SlotPlugin from 'path/to/plugin'
Vue.use(SlotPlugin)
//...rest of code
Run Code Online (Sandbox Code Playgroud)

  • 这很酷,但由于各种边缘情况、scopedSlots、默认槽内部内容、命名槽等而存在滑坡;以我的拙见,包装插槽似乎是一种反模式,最好询问组件中是否存在要包装的插槽(this.$slots)。很好奇您的解决方案此后如何演变。 (2认同)