确定插槽内容是否为空

16
我有一个小的Loading组件,其默认文本应为“加载中...”。使用slots是一个好的选择,所以我的模板如下:

加载中...

这使我可以通过像搜索中... 这样的方式改变加载消息。然而,我想要的行为不仅仅是在没有提供slot内容时显示默认消息,还包括如果slot内容为空或空白时也显示默认消息。目前,如果我执行类似于{{loadingMessage}}的操作,并且 loadingMessage 为null,则不会显示任何文本(而我希望显示默认文本)。因此,理想情况下,我需要测试this.$slots.default来查看是否传入了内容,但是如何找出它是否为空呢?this.$slots.default.text返回undefined。
3个回答

28

你需要一个计算属性来检查this.$slots。对于默认插槽,您需要检查this.$slots.default,而对于具名插槽,只需将default替换为插槽名称。

computed: {
  slotPassed() {
    return !!this.$slots.default[0].text.length
  }
}

然后在您的模板中使用它:

<template>
  <div>
    <slot v-if="slotPassed">Loading...</slot>
    <p v-else>Searching...</p>
  </div>
</template>

你可以在这里看到一个小例子。注意如何显示回滚内容而不是插槽内的“默认内容”。


编辑:我的措辞可能可以更好。你需要检查$slots.X值,但计算属性是一种检查的方法。你也可以直接在模板中编写插槽检查:

<template>
  <div>
    <slot v-if="!!$slots.default[0].text">Loading...</slot>
    <p v-else>Searching...</p>
  </div>
</template>

编辑2:正如@GoogleMac在评论中指出的那样,对于无法渲染文本的组件(例如<transition><keep-alive>等),检查插槽的文本属性会失败,因此他们建议的检查方式是:


!!this.$slots.default && !!this.$slots.default[0]

// or..
!!(this.$slots.default || [])[0]

1
我看到的问题是$slots.default.text始终为未定义。这是一个jsfiddle:https://jsfiddle.net/JohnMoore/rmafyz2x/2/ - John Moore
1
我的错,$slots.default 是一个数组,所以您需要的是 $slots.default[0].text :) - kano
1
是的,这就是我一直在寻找的答案。只要我能测试 $slots.default[0].text,我就可以实现我想要的东西。 - John Moore
3
对我而言,已经过去了一个插槽,但是上述的答案仍然失败了,因为该插槽是一个带有另一个元素的Vue <transition>元素。 过渡元素具有undefined文本属性。在我的情况下,我有一个计算属性,返回!!this.$slots.default && !!this.$slots.default[0] - parker_codes
1
@GoogleMac 哦,这是一个让我今日所学的时刻!感谢分享,我已经编辑了我的回答并加入了你的见解。 - kano

6
@kano的回答很好,但有一个问题:`this.$slots`不是响应式的,所以如果它一开始是`false`,然后变成`true`,任何`computed`属性都不会更新。
解决方案是不依赖于`computed`值,而是依赖于`created`和`beforeUpdated`(正如@MathewSonke指出的那样)。
export default {
  name: "YourComponentWithDynamicSlot",
  data() {
    return {
      showFooter: false,
      showHeader: false,
    };
  },
  created() {
    this.setShowSlots();
  },
  beforeUpdate() {
    this.setShowSlots();
  },
  methods: {
    setShowSlots() {
      this.showFooter = this.$slots.footer?.[0];
      this.showHeader = this.$slots.header?.[0];
    },
  },
};

更新:Vue 3(组合式 API)

对于Vue 3,似乎检查插槽是否有内容的方法已更改(使用新的组合式 API):

import { computed, defineComponent } from "vue";

export default defineComponent({
  setup(_, { slots }) {
    const showHeader = computed(() => !!slots.header);

    return {
      showHeader,
    };
  },
});

注意:我找不到任何关于这个的文档,所以请谨慎对待,但在我非常有限的测试中似乎可以工作。

对于Vue 2,如果您正在使用作用域插槽,则引用实际上是一个函数,而不是对象。因此,在检查子元素之前,必须执行它。 const scopedSlot = this.$scopedSlots.myScopedSlot; const scopedSlotResolved = scopedSlot(); const hasChild = !!(scopedSlotResolved?.[0]); 然后可以进行空值检查,因为作用域插槽引用直到生命周期后期才会成为函数。 - Siegen

0

this.$slots 可以被检查以查看是否使用了插槽。

需要注意的是,this.$slots 不具备响应性。当在计算属性中使用 this.$slots 时可能会出现问题。

https://v2.vuejs.org/v2/api/?redirect=true#:~:text=Please%20note%20that%20slots%20are%20not%20reactive

这意味着我们需要确保每当组件重新渲染时都要检查 this.slots。我们可以通过使用方法而不是计算属性来轻松实现这一点。

https://v2.vuejs.org/v2/guide/computed.html?redirect=true#:~:text=In%20comparison%2C%20a%20method%20invocation%20will%20always%20run%20the%20function%20whenever%20a%20re%2Drender%20happens

<template>
    <div>
        <slot v-if="hasHeading" name="heading"/>
    </div>
</template>


<script>
export default{
    name: "some component",
    methods: {
        hasHeading(){ return !!this.slots.heading}
    }
}
</script>

我们可以安全地将<slot v-if="hasHeading" .../>简化为<slot v-if="!!$slots.heading" .../>,并且跳过创建一个方法吗? - alfreema

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接