Existe-t-il un moyen d'afficher un emplacement uniquement s'il contient du contenu?
Par exemple, je construis un composant Card.vue
simple et je veux que le pied de page ne soit affiché que si son emplacement contient du contenu:
Modèle:
<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>
Scénario:
<script>
export default {
props: {
active: true,
type: {
type: String,
default: 'default',
},
},
computed: {
panelType() {
return `panel-${this.type}`;
},
hasFooterSlot() {
return this.$slots['footer']
}
}
}
</script>
En vue:
<card type="success"></card>
Puisque le composant ci-dessus ne contient pas de pied de page, il ne doit pas être rendu, mais c'est le cas.
J'ai essayé d'utiliser this.$slots['footer']
, mais cela retourne undefined.
Est-ce que quelqu'un a des conseils?
Il devrait être disponible à
this.$slots.footer
Donc, cela devrait fonctionner.
hasFooterSlot() {
return !!this.$slots.footer
}
Exemple .
Vous devriez vérifier vm.$slots
et aussi vm.$scopedSlots
pour cela.
hasSlot (name = 'default') {
return !!this.$slots[ name ] || !!this.$scopedSlots[ name ];
}
J'espère que j'ai bien compris. Pourquoi ne pas utiliser une balise <template>
, qui n'est pas rendue, si l'emplacement est vide.
<slot name="foo"></slot>
Utilisez-le comme ceci:
<template slot="foo">
...
</template>
J'ai rencontré un problème similaire, mais sur une base de code large et lors de la création de composants structurés de conception atomique, il peut être fastidieux d'écrire des méthodes hasSlot()
tout le temps et quand il s'agit de TDD - c'est une méthode de plus pour tester ... En disant cela, vous peut toujours mettre la logique brute dans un v-if
mais j’ai trouvé que le modèle finissait par être encombré et difficile à lire à certaines occasions, en particulier pour un nouveau développeur vérifiant la structure du code.
On m'a demandé de trouver un moyen de supprimer les div
s de logements parent lorsque le logement n'est pas fourni.
Problème:
<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>
comme vous pouvez le constater, le problème est que j'ai un div presque "en queue" qui peut poser des problèmes de style lorsque l'auteur du composant décide qu'il n'est pas nécessaire d'utiliser un emplacement bar
.
bien sûr, nous pourrions utiliser <div v-if="$slots.bar">...</div>
ou <div v-if="hasBar()">...</div>
etc., mais comme je l'ai dit - cela peut devenir fastidieux et finir par être plus difficile à lire.
Solution
Ma solution consistait à créer un composant slot
générique qui rendait simplement un emplacement avec un div entourant ... voir ci-dessous.
//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>
J'ai rencontré des problèmes de cas d'utilisation en essayant cette idée et parfois c'était ma structure de balisage qui devait changer pour profiter de cette approche . Cette approche réduit le besoin de contrôles de petite taille dans chaque modèle de composant. Je suppose que vous pouvez voir le composant sous la forme d'un composant <conditional-div />
...
Il est également intéressant de noter que l’application d’attributs à l’instanciation slot-component
(<slot-component class="myClass" data-random="randomshjhsa" />
) convient parfaitement, car les attributs s’inscrivent dans le div contenant du modèle slot-component
.
J'espère que cela t'aides.
UPDATE J'ai écrit un plugin pour cela, il n'est donc plus nécessaire d'importer le composant custom-slot
dans chaque composant consommateur et il vous suffira d'écrire Vue.use (SlotPlugin) dans votre main.js. instanciation. (voir ci-dessous)
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
En bref, faites ceci en ligne:
<template lang="pug">
div
h2(v-if="$slots.title")
slot(name="title")
h3(v-if="$slots['sub-title']")
slot(name="sub-title")
</template>