Modul 3: Komponenten

Slots

Flexible Komponenten-Layouts durch Content-Projektion — Vues mächtigstes Kompositions-Werkzeug.

Was sind Slots?

Slots ermöglichen es, beliebigen Inhalt in eine Komponente hineinzuprojizieren. Statt nur Daten per Props zu übergeben, kannst du ganzes HTML oder andere Komponenten einbetten. Das macht Slots zum Werkzeug für wiederverwendbare Layouts.

🛤️

Rails-Vergleich: yield in Layouts

In Rails-Layouts nutzt du <%= yield %>, um den Seiteninhalt einzufügen. Benannte Slots entsprechen <%= yield :sidebar %> mit content_for :sidebar. Gleiche Idee, mehr Flexibilität.

Der Standard-Slot

Der einfachste Fall: Ein <slot /> in der Kind-Komponente wird durch den Inhalt ersetzt, den die Eltern-Komponente dazwischen schreibt.

AlertBox.vue
<template>
  <div class="alert-box">
    <strong>⚠️ Achtung:</strong>
    <slot />
  </div>
</template>
Verwendung
<template>
  <AlertBox>
    <p>Das Formular enthält ungültige Daten.</p>
  </AlertBox>

  <!-- Alles zwischen den Tags wird in <slot /> eingefügt -->
</template>

Fallback-Inhalt

Du kannst Standard-Inhalte definieren, die angezeigt werden, wenn die Eltern-Komponente keinen Inhalt übergibt.

SubmitButton.vue
<template>
  <button class="btn" type="submit">
    <!-- Fallback-Inhalt, wenn nichts übergeben wird -->
    <slot>Absenden</slot>
  </button>
</template>

<!-- Verwendung: -->
<!-- <SubmitButton />          → zeigt "Absenden"           -->
<!-- <SubmitButton>Jetzt kaufen</SubmitButton> → zeigt "Jetzt kaufen" -->

Benannte Slots

Wenn du mehrere Inhaltsbereiche brauchst, gibst du jedem Slot einen Namen. Die Eltern-Komponente füllt sie mit v-slot: oder der Kurzform #.

PageLayout.vue
<template>
  <div class="page-layout">
    <header>
      <slot name="header" />
    </header>

    <main>
      <!-- Unbenannt = default slot -->
      <slot />
    </main>

    <aside>
      <slot name="sidebar">
        <!-- Fallback -->
        <p>Keine Sidebar definiert.</p>
      </slot>
    </aside>

    <footer>
      <slot name="footer" />
    </footer>
  </div>
</template>
Verwendung mit #-Kurzform
<template>
  <PageLayout>
    <template #header>
      <h1>Meine Seite</h1>
      <nav>...</nav>
    </template>

    <!-- Default-Slot (kein #-Name nötig) -->
    <article>
      <p>Hauptinhalt der Seite.</p>
    </article>

    <template #sidebar>
      <WidgetList />
    </template>

    <template #footer>
      <p>© 2025 Mein Projekt</p>
    </template>
  </PageLayout>
</template>
💡

Kurzschreibweise

v-slot:header kann als #header geschrieben werden. Der Default-Slot heißt #default.

Scoped Slots — Daten zurückgeben

Scoped Slots geben Daten von der Kind-Komponente an den Slot-Inhalt zurück. So kann die Eltern-Komponente entscheiden, wie die Daten dargestellt werden, während die Kind-Komponente die Daten bereitstellt.

UserList.vue
<script setup lang="ts">
interface User {
  id: number
  name: string
  role: string
}

defineProps<{
  users: User[]
}>()
</script>

<template>
  <ul>
    <li v-for="user in users" :key="user.id">
      <!-- Daten an den Slot zurückgeben -->
      <slot :user="user" :is-admin="user.role === 'admin'">
        <!-- Fallback-Darstellung -->
        {{ user.name }}
      </slot>
    </li>
  </ul>
</template>
Verwendung
<template>
  <UserList :users="users">
    <!-- Destrukturierung der Slot-Props -->
    <template #default="{ user, isAdmin }">
      <div class="user-card">
        <strong>{{ user.name }}</strong>
        <span v-if="isAdmin" class="badge">Admin</span>
      </div>
    </template>
  </UserList>
</template>
ℹ️

Renderless Components

Das Scoped-Slot-Pattern wird für Renderless Components genutzt — Komponenten, die nur Logik bereitstellen und kein eigenes HTML rendern. Beispiel: Eine <MouseTracker>-Komponente, die x/y über einen Scoped Slot verfügbar macht.

Interaktive Demo

Eine flexible Karten-Komponente mit Header-, Body- und Footer-Slots. Schalte die einzelnen Bereiche an und ab:

Flexible Karten-Slots
Interaktiv

#header

#default
Hier steht der Hauptinhalt der Karte. Dieser Bereich ist immer sichtbar, weil er den Standard-Slot nutzt.

#footer →
<Card> <template #header> <h3>Kartentitel</h3> </template> <p>Hauptinhalt der Karte...</p> <template #footer> <Button label="Abbrechen" /> <Button label="Speichern" primary /> </template> </Card>

Zusammenfassung

ℹ️

Kernpunkte

  • <slot /> projiziert Eltern-Inhalt in Kind-Komponenten
  • Benannte Slots (#header, #footer) für mehrere Bereiche
  • Scoped Slots geben Daten an den Slot-Inhalt zurück
  • Fallback-Inhalte werden angezeigt, wenn kein Inhalt übergeben wird
  • Renderless Components nutzen Scoped Slots für reine Logik-Komponenten