Das JavaScript-Framework Vue.js, Teil 15 Flexible Vue-Komponenten mit Slots

Von Dr. Dirk Koller

Im fünften Teil der Vue.js-Reihe haben wir die Props-Übergabe vorgestellt, die sich aber nur für einfache Übergaben eignet. Slots hingegen injizieren ganze UI-Elemente in Child-Komponenten, wie dieser Beitrag zeigt.

Die Übergabe komplexerer UI-Elemente lässt sich in Vue.js über Slots realisieren.
Die Übergabe komplexerer UI-Elemente lässt sich in Vue.js über Slots realisieren.
(Bild: RealToughCandy.com / Pexels)

Mithilfe der Props-Übergabe von Parent- an Child-Komponenten lassen sich bis zu einem gewissen Grad auch Unterschiede in der Darstellung, also am Content von Komponenten konfigurieren. Einer Komponente, die beispielsweise einen Textbereich enthält, könnte so mitgeteilt werden, aus wie vielen Zeilen die Text Area bestehen soll. Das folgende Codestück demonstriert das an einer einfachen Child-Komponente:

<template>
  <div>
    <h1>In Child</h1>
    <textarea v-bind:rows="myRows"></textarea>
  </div>
</template>
<script>
export default {
  props: ["myRows"],
  setup(props) {},
};
</script>

Die Übergabe der Prop myRows in der Parent-Komponente sieht folgendermaßen aus:

<template>
  <h1>In Parent</h1>
  <Child myRows="5"></Child>
</template>

Für einfache Fälle mag das genügen. Hat man aber komplexe UI-Komponenten in der Child-Komponente (zum Beispiel aus der Vuetify-Bibliothek), müssten sehr viele Props übergeben werden. Und noch flexiblere Komponenten, deren Inhalt in Form von HTML-Elementen oder anderen Vue-Komponenten komplett von der Parent-Komponente definiert werden sollen, sind so gleich gar nicht machbar.

Hilfreich wäre eine Möglichkeit, einer Komponente Content zu übergeben – eine Art Schacht in der Komponente, in den ein Fragment hineingeworfen werden kann. Der naheliegende Ansatz, den darzustellenden Content zwischen öffnendem und schließendem Tag der Child-Komponente im Parent-Template anzugeben, funktioniert erst einmal nicht. Der Inhalt (hier der Text „Neuer Content“) wird einfach durch das Template der Child-Komponente ersetzt:

<script setup>
  import Child from "./Child.vue";
</script>
<template>
  <h1>In Parent</h1>
  <Child>Neuer Content</Child>
</template>

Einfache Slots

An dieser Stelle haben nun Slots ihren großen Auftritt. Definiert man innerhalb des Template der Child-Komponente einen Slot mithilfe des slot-Elements, ermöglicht dieser exakt das gewünschte Verhalten: Der im Parent-Template zwischen öffnendem und schließendem Child-Komponenten-Tag angegebene Content wird in der Child-Komponente anstelle des Slots gerendert:

<template>
  <div>
    <h1>In Child</h1>
    <slot/>
  </div>
</template>

Auf diesem Weg lassen sich ganze UI-Elemente wie etwa ein Button in die Child-Komponente injizieren:

<template>
  <h1>In Parent</h1>
  <Child><button @click="buttonPresed">Klick</button></Child>
</template>

Dabei angegebene Handler-Methoden wie hier für das Click-Event werden dabei übrigens in der Parent-Komponente gesucht (alles im Parent-Template wird auch im Parent-Scope kompiliert).

Slots mit Fallback-Content

Es gibt Fälle, in denen es der Parent-Komponente unmöglich ist, den Slot zu füllen. Denkbar ist zum Beispiel eine Child-Komponente mit einem Slot, in dem Daten auf verschiedene Art visualisiert werden (Tabellen, Charts usw.). Was ist aber, wenn gar keine Daten vorliegen? In diesen Fällen kann es hilfreich sein, einen Platzhalter anzuzeigen. Dies geschieht durch Angabe eines Default-Content zwischen öffnendem und schließenden slot-Element:

<template>
  <div>
    <h1>In Child</h1>
    <slot>Keine Daten verfügbar</slot>
  </div>
</template>

Wird der Slot nun nicht gefüllt, ist hier der Text „Keine Daten verfügbar” zu sehen.

Den Slot beim Namen nennen

Mit den oben beschriebenen einfachen Slots lässt sich nur ein Bereich in der Child-Komponente befüllen. Oft hat man aber den Fall, dass es davon mehrere gibt. Glücklicherweise lassen sich Slots benamen. Dies geschieht mit dem name-Attribut:

<template>
  <div>
    <h1>In Child</h1>
    <slot name="slot1"></slot>
    <slot name="slot2"></slot>
  </div>
</template>

Das Ansprechen der Slots in der Parent-Komponente erfolgt mithilfe des Template-Elements und einer Direktive namens v-slot:

<template>
  <h1>In Parent</h1>
  <Child>
    <template v-slot:slot1> Content für Slot 1 </template>
    <template v-slot:slot2> Content für Slot 2 </template>
  </Child>
</template>

Nicht benamte Slots haben übrigens intern auch einen Namen, sie heißen ‚default‘.

Scoped Slots & Slot-Props

Neben den Named-Slots gibt es Slots noch in einer weiteren spannenden Geschmacksrichtung: Die Scoped-Slots. Sie ermöglichen dem Slot-Content in der Parent-Komponente auf Daten der Child-Komponente zuzugreifen. Dies geschieht durch Übergabe von Properties aus der Child-Komponente in den Slot, wo sie von der Parent-Komponente erreichbar sind.

Klingt zunächst einmal kompliziert, man versteht das am besten anhand eines Beispiels. In der folgenden Child-Komponte wird eine Property myText mit einem String-Inhalt definiert. Mithilfe eines Attribute-Bindings werden die Daten über den Slot als Slot-Prop mit Namen myProp an die Parent-Komponente übergeben:

<template>
  <div>
    <h1>In Child</h1>
    <slot v-bind:myProp="myText"></slot>
  </div>
</template>
<script>
import { ref } from "vue";
export default {
  setup() {
    const myText = ref("Daten aus der Child-Komponente");
    return { myText };
  },
};
</script>

Die Parent-Komponente bekommt im Template-Element die Slot-Props durch das v-slot-Attribut bekannt gemacht und kann anschließend auf sämtliche Slot-Props, darunter myProp, zugreifen:

<script setup>
import Child from "./Child.vue";
</script>
<template>
  <h1>In Parent</h1>
  <Child>
    <template v-slot="mySlotProps">
      <p>{{ mySlotProps.myProp }}</p>
    </template>
  </Child>
</template>

In Summe handelt es sich also um eine Art umgekehrte Property-Übergabe vom Child an den Parent. Nicht ganz einfach, aber durchaus nützlich.

Jetzt Newsletter abonnieren

Täglich die wichtigsten Infos zu Softwareentwicklung und DevOps

Mit Klick auf „Newsletter abonnieren“ erkläre ich mich mit der Verarbeitung und Nutzung meiner Daten gemäß Einwilligungserklärung (bitte aufklappen für Details) einverstanden und akzeptiere die Nutzungsbedingungen. Weitere Informationen finde ich in unserer Datenschutzerklärung.

Aufklappen für Details zu Ihrer Einwilligung

Slots sind mächtige Bausteine im Vue.js-Framework, die Komponenten eine enorme Flexibilität verleihen können. Wie eingangs erwähnt lassen sich aber auch viele Probleme durch einfache Props lösen. Props sind immer dann besser geeignet, wenn der Content gleich bleibt und sich durch wenige übergebene Werte anpassen lässt. Weitere Informationen zu Slots finden sich im Component-Guide der Vue.js-Doku.

(ID:48522741)