# Slots

Cette page suppose que vous avez déjà lu les Principes de base des composants. Lisez-le d'abord si vous n'êtes pas familier avec les composants.

# Contenu de Slot

Vue implémente une API de distribution de contenu inspirée du projet de spécification des composants Web (opens new window), en utilisant l'élément <slot> pour servir de points de distribution pour le contenu.

Cela vous permet de composer des composants comme celui-ci:

<todo-button>
  Ajouter todo
</todo-button>
1
2
3

Ensuite, dans le template pour <todo-button>, vous pourriez avoir:

<!-- template du composant `todo-button` -->
<button class="btn-primary">
  <slot></slot>
</button>
1
2
3
4

Lors du rendu du composant, «» sera remplacé par «Ajouter todo».

<!-- HTML généré -->
<button class="btn-primary">
  Ajouter todo
</button>
1
2
3
4

Les chaînes de caractères ne sont que le début! Les emplacements peuvent également contenir n'importe quel code de template, y compris du HTML:

<todo-button>
  <!-- Ajouter une icône Font Awesome -->
  <i class="fas fa-plus"></i>
  Ajouter todo
</todo-button>
1
2
3
4
5

Ou même un autre composant

<todo-button>
  <!-- Utiliser un composant pour ajouter une icône -->
  <font-awesome-icon name="plus"></font-awesome-icon>
  Ajouter todo
</todo-button>
1
2
3
4
5

Si le template de <todo-button> ne contenait pas d'élément <slot>, tout contenu fourni entre sa balise d'ouverture et de fermeture serait ignoré.

<!-- template de composant todo-button -->

<button class="btn-primary">
  Créer un nouvel item
</button>
1
2
3
4
5
<todo-button>
  <!-- Le texte suivant sera ignoré -->
  Ajouter todo
</todo-button>
1
2
3
4

# Scope de rendu

Lorsque vous souhaitez utiliser des données à l'intérieur d'un slot, comme dans:

<todo-button>
  Supprimer un {{ item.name }}
</todo-button>
1
2
3

Cet slot a accès aux mêmes propriétés d'instance (c'est-à-dire à la même «scope») que le reste du template.

Slot explanation diagram

Le slot n'a pas accès à la scope de <todo-button>. Par exemple, essayer d'accéder à action ne fonctionnerait pas:

<todo-button action="effacer">
  Cliquer ici pour {{ action }} un élément
  <!--
  L'`action` ne sera pas définie, car ce contenu est passé
  _à_ <todo-button>, plutôt que défini _à l'intérieur_ du
  Composant <todo-button>.
  -->
</todo-button>
1
2
3
4
5
6
7
8

En règle générale, rappelez-vous que:

Tout dans le template parent est compilé dans la scope parent; tout dans le template enfant est compilé dans la scope enfant.

# Contenu de secours

Il y a des cas où il est utile de spécifier le contenu de secours (c'est-à-dire par défaut) pour un slot, à restituer uniquement lorsqu'aucun contenu n'est fourni. Par exemple, dans un composant <submit-button>:

<button type="submit">
  <slot></slot>
</button>
1
2
3

Nous pourrions vouloir que le texte "Envoyer" soit affiché dans le <button> la plupart du temps. Pour faire de "Envoyer" le contenu de secours, nous pouvons le placer entre les balises <slot>:

<button type="submit">
  <slot>Envoyer</slot>
</button>
1
2
3

Maintenant quand nous utilisons <submit-button> dans un composant parent, ne fournissant aucun contenu pour le slot:

<submit-button></submit-button>
1

génèrera le contenu de remplacement, "Envoyer":

<button type="submit">
  Envoyer
</button>
1
2
3

Mais si nous fournissons du contenu:

<submit-button>
  Sauvegarder
</submit-button>
1
2
3

Alors le contenu fourni sera affiicher à la place:

<button type="submit">
  Sauvegarder
</button>
1
2
3

# Slots nommés

Il y a des moments où il est utile d'avoir plusieurs slots. Par exemple, dans un composant <base-layout> avec le template suivant:

<div class="container">
  <header>
    <!-- Nous voulons le contenu de l'en-tête ici -->
  </header>
  <main>
    <!-- Nous voulons le contenu principal ici -->
  </main>
  <footer>
    <!-- Nous voulons le contenu du pied de page ici -->
  </footer>
</div>
1
2
3
4
5
6
7
8
9
10
11

Dans ces cas, l'élément <slot> a un attribut spécial, name, qui peut être utilisé pour attribuer un ID unique à différents emplacements afin que vous puissiez déterminer où le contenu doit être généré:

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
1
2
3
4
5
6
7
8
9
10
11

Une <slot> sans name a implicitement le nom "default".

Pour fournir du contenu aux slots nommés, nous devons utiliser la directive v-slot sur un élément<template>, en fournissant le nom du slot comme argument de v-slot:

<base-layout>
  <template v-slot:header>
    <h1>Voici un titre de page</h1>
  </template>

  <template v-slot:default>
    <p>Un paragraphe pour le contenu principal.</p>
    <p>Et un autre.</p>
  </template>

  <template v-slot:footer>
    <p>Voici quelques infos de contacts</p>
  </template>
</base-layout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Maintenant, tout ce qui se trouve à l'intérieur des éléments <template> sera passé aux emplacements correspondants.

Le HTML rendu sera:

<div class="container">
  <header>
    <h1>Voici un titre de page</h1>
  </header>
  <main>
    <p>Un paragraphe pour le contenu principal.</p>
    <p>Et un autre.</p>
  </main>
  <footer>
    <p>Voici quelques infos de contacts</p>
  </footer>
</div>
1
2
3
4
5
6
7
8
9
10
11
12

Notez que v-slot ne peut être ajouté qu'à un <template> (avec une exception)

# Slots scopés

Parfois, il est utile que le contenu du slot ait accès aux données disponibles uniquement dans le composant enfant. C'est un cas courant lorsqu'un composant est utilisé pour afficher un tableau d'items, et nous voulons pouvoir personnaliser la façon dont chaque item est généré.

Par exemple, nous avons un composant, contenant une liste de todo-items.

app.component('todo-list', {
  data() {
    return {
      items: ['Feed a cat', 'Buy milk']
    }
  },
  template: `
    <ul>
      <li v-for="(item, index) in items">
        {{ item }}
      </li>
    </ul>
  `
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Nous pourrions vouloir remplacer le {{ item }} par un <slot> pour le personnaliser sur le composant parent:

<todo-list>
  <i class="fas fa-check"></i>
  <span class="green">{{ item }}</span>
</todo-list>
1
2
3
4

Cela ne fonctionnera pas cependant, car seul le composant <todo-list> a accès à item et nous fournissons le contenu du slot à partir de son parent.

Pour rendre item disponible au contenu du slot fourni par le parent, nous pouvons ajouter un élément <slot> et le lier en tant qu'attribut:

<ul>
  <li v-for="( item, index ) in items">
    <slot :item="item"></slot>
  </li>
</ul>
1
2
3
4
5

Vous pouvez lier autant d'attributs au slot que nécessaire:

<ul>
  <li v-for="( item, index ) in items">
    <slot
      :item="item"
      :index="index"
      :another-attribute="anotherAttribute"
    ></slot>
  </li>
</ul>
1
2
3
4
5
6
7
8
9

Les attributs liés à un élément <slot> sont appelés props de slot. Maintenant, dans la scope parent, nous pouvons utiliser v-slot avec une valeur pour définir un nom pour les props de slot qui nous ont été fournis:

<todo-list>
  <template v-slot:default="slotProps">
    <i class="fas fa-check"></i>
    <span class="green">{{ slotProps.item }}</span>
  </template>
</todo-list>
1
2
3
4
5
6
Scoped slot diagram

Dans cet exemple, nous avons choisi de nommer l'objet contenant tous nos props de slot slotProps, mais vous pouvez utiliser le nom de votre choix.

# Syntaxe abrégée pour les slots par défaut isolés

Dans les cas comme ci-dessus, lorsque seul le slot par défaut est fourni en contenu, les balises du composant peuvent être utilisées comme template du slot. Cela nous permet d'utiliser v-slot directement sur le composant:

<todo-list v-slot:default="slotProps">
  <i class="fas fa-check"></i>
  <span class="green">{{ slotProps.item }}</span>
</todo-list>
1
2
3
4

Cela peut être raccourci encore plus. Tout comme le contenu non spécifié est supposé être pour le slot par défaut, v-slot sans argument est supposé faire référence au slot par défaut:

<todo-list v-slot="slotProps">
  <i class="fas fa-check"></i>
  <span class="green">{{ slotProps.item }}</span>
</todo-list>
1
2
3
4

Notez que la syntaxe abrégée de la slot par défaut ne peut pas être mélangée avec des slots nommés, car cela entraînerait une ambiguïté de scope:

<!-- INVALIDE, il en résultera un avertissement-->
<todo-list v-slot="slotProps">
  <i class="fas fa-check"></i>
  <span class="green">{{ slotProps.item }}</span>

  <template v-slot:other="otherSlotProps">
    slotProps n'est PAS disponible ici
  </template>
</todo-list>
1
2
3
4
5
6
7
8
9

Chaque fois qu'il y a plusieurs slots, utilisez la syntaxe complète basée sur <template> pour tous les slots:

<todo-list>
  <template v-slot:default="slotProps">
    <i class="fas fa-check"></i>
    <span class="green">{{ slotProps.item }}</span>
  </template>

  <template v-slot:other="otherSlotProps">
    ...
  </template>
</todo-list>
1
2
3
4
5
6
7
8
9
10

# Destructuring des slots props

En interne, les slots scopés fonctionnent en enveloppant le contenu de votre slot dans une fonction avec un seul argument:

function (slotProps) {
  // ... contenu du slot ...
}
1
2
3

Cela signifie que la valeur de v-slot peut en fait accepter toute expression JavaScript valide qui peut apparaître à la position d'argument d'une définition de fonction. Ainsi, vous pouvez également utiliser le destructuring de ES2015 (opens new window) pour extraire des props de slot spécifiques, comme ceci:

<todo-list v-slot="{ item }">
  <i class="fas fa-check"></i>
  <span class="green">{{ item }}</span>
</todo-list>
1
2
3
4

Cela peut rendre le template beaucoup plus propre, en particulier lorsque le slot fournit de nombreux props. Cela ouvre également d'autres possibilités, telles que le changement de nom des props, par ex. item à todo:

<todo-list v-slot="{ item: todo }">
  <i class="fas fa-check"></i>
  <span class="green">{{ todo }}</span>
</todo-list>
1
2
3
4

Vous pouvez même définir des solutions de secours, à utiliser dans le cas où un prop de slot n'est pas défini:

<todo-list v-slot="{ item = 'Placeholder' }">
  <i class="fas fa-check"></i>
  <span class="green">{{ item }}</span>
</todo-list>
1
2
3
4

# Noms de slot dynamiques

Les Arguments de directive dynamique fonctionnent également sur v-slot, permettant la définition de noms de slots dynamiques:

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>
</base-layout>
1
2
3
4
5

# Abréviation des slots nommés

Semblable à v-on etv-bind, v-slot a également un raccourci, remplaçant tout avant l'argument (v-slot:) par le symbole spécial #.Par exemple, v-slot:header peut être réécrit #header:

<base-layout>
  <template #header>
    <h1>Voici un titre de page</h1>
  </template>

  <template #default>
    <p>Un paragraphe pour le contenu princip.</p>
    <p>Et un autre.</p>
  </template>

  <template #footer>
    <p>Voici quelques infos de contacts</p>
  </template>
</base-layout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Cependant, tout comme pour les autres directives, le raccourci n'est disponible que lorsqu'un argument est fourni. Cela signifie que la syntaxe suivante n'est pas valide:

<!-- Ceci déclenchera un avertissement -->

<todo-list #="{ item }">
  <i class="fas fa-check"></i>
  <span class="green">{{ item }}</span>
</todo-list>
1
2
3
4
5
6

Au lieu de cela, vous devez toujours spécifier le nom du slot si vous souhaitez utiliser le raccourci:

<todo-list #default="{ item }">
  <i class="fas fa-check"></i>
  <span class="green">{{ item }}</span>
</todo-list>
1
2
3
4

Deployed on Netlify.
Dernière Mise-à-jour: 3/19/2021, 8:08:18 PM