Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Eigene Alpine-Komponente bauen: State, Events, Interaktivität ohne separate JS-Datei

Eigene Alpine-Komponente bauen: State, Events, Interaktivität ohne separate JS-Datei

~8 Min. Lesezeit Zuletzt aktualisiert am 9. August 2026

Zum Abschluss von Block 4 bauen wir eine eigene, kleine Alpine-Komponente von Grund auf - einen einklappbaren "Akkordeon"-Bereich, wie man ihn zum Beispiel für FAQ-Abschnitte braucht. Das Muster ist bewusst so gewählt, dass es sich später leicht auf die Kategorie-Filterung der Team-Seite (Kapitel 21) übertragen lässt.

Schritt 1: den Zustand definieren

Zuerst überlegen wir, welcher Zustand überhaupt gebraucht wird. Für ein Akkordeon reicht eine einzige Information: welcher Eintrag (falls überhaupt einer) gerade geöffnet ist.

<div x-data="{ openIndex: null }">
    <!-- Einträge folgen -->
</div>

Schritt 2: die Einträge und die Toggle-Logik

<div x-data="{ openIndex: null }">
    <?php foreach ($faqViewModel->getEntries() as $index => $entry): ?>
        <div class="border-b border-slate-200">
            <button
                type="button"
                @click="openIndex = openIndex === <?= (int) $index ?> ? null : <?= (int) $index ?>"
                class="flex w-full items-center justify-between py-4 text-left font-semibold text-brand-dark"
            >
                <?= $escaper->escapeHtml($entry['question']) ?>
                <span x-text="openIndex === <?= (int) $index ?> ? '−' : '+'"></span>
            </button>
            <p x-show="openIndex === <?= (int) $index ?>" x-cloak class="pb-4 text-brand-slate">
                <?= $escaper->escapeHtml($entry['answer']) ?>
            </p>
        </div>
    <?php endforeach; ?>
</div>

Die Kernlogik steckt im @click-Ausdruck: Ist der geklickte Eintrag bereits offen, wird er geschlossen (null), sonst wird er zum neuen offenen Eintrag. So bleibt immer höchstens ein Eintrag gleichzeitig geöffnet.

Schritt 3: eine benannte Alpine.data()-Komponente statt Inline-Objekt

Für sehr einfache Fälle reicht das Inline-Objekt in x-data völlig aus. Wird die Logik komplexer (mehrere Methoden, wiederverwendbar an mehreren Stellen der Seite), lohnt sich eine benannte Komponente über Alpine.data() in einem Inline-<script>-Block - mit korrekter CSP-Registrierung:

Benannte Komponente statt Inline-Objekt
<script>
    document.addEventListener('alpine:init', () => {
        Alpine.data('faqAccordion', () => ({
            openIndex: null,
            toggle(index) {
                this.openIndex = this.openIndex === index ? null : index;
            },
            isOpen(index) {
                return this.openIndex === index;
            },
        }));
    });
</script>
<?php $hyvaCsp->registerInlineScript(); ?>

<div x-data="faqAccordion">
    <!-- @click="toggle(0)", x-show="isOpen(0)" usw. -->
</div>

Der entscheidende Vorteil: toggle() und isOpen() sind lesbare Methodennamen statt eines langen Inline-Ausdrucks, und die Logik ist an einer Stelle gebündelt statt über mehrere x-data-Objekte im Template verstreut.

Tipp: Faustregel: ein einfaches Inline-Objekt in x-data="{ ... }", solange es nur ein oder zwei Zustandsvariablen und triviale Ausdrücke sind. Sobald echte Methoden mit mehreren Zeilen Logik nötig sind, lohnt sich Alpine.data().

Bereit für das durchgehende Projekt

Mit Layout-XML, ViewModels, Escaping, CSP, Tailwind und Alpine sind jetzt alle Bausteine vorhanden, die für eine eigene Storefront-Seite gebraucht werden. Ab Kapitel 17 setzen wir genau das in einem einzigen, durchgehenden Projekt zusammen: einer Team-Seite mit Kategorie-Filter.