web-dev-qa-db-fra.com

Afficher uniquement le slot s'il a du contenu

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?

8
Steve Bauman

Il devrait être disponible à 

this.$slots.footer

Donc, cela devrait fonctionner.

hasFooterSlot() {
  return !!this.$slots.footer
}

Exemple .

17
Bert

Vous devriez vérifier vm.$slots et aussi vm.$scopedSlots pour cela.

hasSlot (name = 'default') {
   return !!this.$slots[ name ] || !!this.$scopedSlots[ name ];
}
3
Madmadi

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>
0
grindking

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 divs 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

0
Francis Leigh

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>
0
Syed