如果有任何内容,是否只能显示一个插槽?
例如,我正在构建一个简单的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>
脚本:
<script>
export default {
props: {
active: true,
type: {
type: String,
default: 'default',
},
},
computed: {
panelType() {
return `panel-${this.type}`;
},
hasFooterSlot() {
return this.$slots['footer']
}
}
}
</script>
在视图中:
<card type="success"></card>
由于上面的组件不包含页脚,因此不应该呈现它,但它是。
我尝试过使用this.$slots['footer']
,但这会返回undefined。
有没有人有任何提示?
答案 0 :(得分:23)
答案 1 :(得分:6)
您应该检查vm.$slots
以及vm.$scopedSlots
。
hasSlot (name = 'default') {
return !!this.$slots[ name ] || !!this.$scopedSlots[ name ];
}
答案 2 :(得分:0)
我遇到了一个类似的问题,但是在广泛的代码库中,当创建原子设计结构化组件时,它总是很难写hasSlot()
方法,而且当涉及到TDD时 - 它还有一种测试方法...说,你总是可以将原始逻辑放在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>
正如您所看到的,问题是我有一个几乎'尾随'的div,当组件作者决定不需要bar
插槽时,它可能会提供样式问题。
当然,我们可以去<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>
我在尝试这个想法时遇到了用例问题,有时我的标记结构需要为了这种方法的利益而改变。
这种方法减少了每个组件模板中小插槽检查的需要。我想你可以将组件看作<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
答案 3 :(得分:0)
希望我明白这一点。如果插槽为空,为什么不使用未呈现的<template>
标记。
<slot name="foo"></slot>
像这样使用:
<template slot="foo">
...
</template>
答案 4 :(得分:0)
简而言之,可以内联:
<template lang="pug">
div
h2(v-if="$slots.title")
slot(name="title")
h3(v-if="$slots['sub-title']")
slot(name="sub-title")
</template>
答案 5 :(得分:0)
最初我以为https://stackoverflow.com/a/50096300/752916可以正常工作,但是由于$ scopeSlots返回的函数始终是真实的,无论其返回值如何,我都不得不对其进行扩展。这是我的解决方案,尽管我得出的结论是,此问题的真正答案是“这样做是一种反模式,如果可能,应避免使用它”。例如。只需制作一个可以插入其中的单独的页脚组件即可。
hasFooterSlot() {
const ss = this.$scopedSlots;
const footerNodes = ss && ss.footer && ss.footer();
return footerNodes && footerNodes.length;
}
const panelComponent = {
template: `
<div class="nice-panel">
<div class="nice-panel-content">
<!-- Slot for main content -->
<slot />
</div>
<!-- Slot for optional footer -->
<slot name="footer"></slot>
</div>
`
}
const footerComponent = {
template: `
<div class="nice-panel-footer">
<slot />
</div>
`
}
var app = new Vue({
el: '#app',
components: {
panelComponent,
footerComponent
},
data() {
return {
name: 'Vue'
}
}
})
.nice-panel {
max-width: 200px;
border: 1px solid lightgray;
}
.nice-panel-content {
padding: 30px;
}
.nice-panel-footer {
background-color: lightgray;
padding: 5px 30px;
text-align: center;
}
<script src="https://unpkg.com/vue@2.6.11/dist/vue.min.js"></script>
<div id="app">
<h1>Panel with footer</h1>
<panel-component>
lorem ipsum
<template #footer>
<footer-component> Some Footer Content</footer-component>
</template>
</panel-component>
<h1>Panel without footer</h1>
<panel-component>
lorem ipsum
</panel-component>
</div>
答案 6 :(得分:0)
CSS 简化了很多。只需使用以下代码,瞧!
.panel-footer:empty {
display: none;
}
答案 7 :(得分:0)
这是 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>