Livewire Laravel

TinyMCE als Livewire Component

Chris
Geschäftsführer, PHP Senior-Entwickler
Aktualisiert:
TinyMce in a Livewire Component.

Laravel Livewire ist eine massive Arbeitserleichterung für dynamische Webapps. Aber bei der Kombination mit externen JavaScript-Bibliotheken wie TinyMCE gibt es eine Falle: Livewire und der Editor kämpfen um die Kontrolle über das DOM.

Das Problem: Livewire zerstört den Editor

Livewire ersetzt bei jedem Server-Roundtrip die DOM-Elemente seiner Komponente. Wenn TinyMCE an ein <textarea> innerhalb einer Livewire-Komponente gebunden ist, wird die TinyMCE-Instanz bei jedem Update zerstört und müsste neu initialisiert werden. Der Editor verliert den Fokus, der Cursor springt, Undo-History geht verloren – in der Praxis unbenutzbar.

Das gleiche Problem betrifft übrigens alle JavaScript-Bibliotheken, die eigene DOM-Elemente erzeugen: Quill, CKEditor, CodeMirror, Select2, Flatpickr. Die Lösung ist identisch.

Die Lösung: wire:ignore + Events

Zwei Mechanismen lösen das Problem:

  1. wire:ignore – Verhindert, dass Livewire den umschlossenen DOM-Bereich bei Updates anfasst. TinyMCE behält die Kontrolle über sein <textarea>.

  2. @this.set() – Über den change-Event von TinyMCE synchronisieren wir den Editor-Inhalt manuell zurück in die Livewire-Property.

Implementation

<!-- Blade-Template -->
<div>
    <div wire:ignore>
        <textarea id="editor" wire:model="document">{{ $document }}</textarea>
    </div>

    <button type="submit" wire:click="save">Speichern</button>

    @script
    <script>
        tinymce.init({
            selector: '#editor',
            license_key: 'gpl',
            setup: function (editor) {
                editor.on('change input', function () {
                    $wire.set('document', editor.getContent());
                });
            }
        });
    </script>
    @endscript
</div>Language:html

Was hier passiert

  • wire:ignore auf dem Container-Div schützt die Textarea vor Livewire-Morphing.

  • @script ist Livewire 3 Syntax – das Script wird automatisch evaluiert, auch wenn die Komponente via Turbolinks/navigate geladen wird. In Livewire 2 müsste man stattdessen window.addEventListener('livewire:load', ...) verwenden.

  • $wire.set() (Livewire 3) ersetzt @this.set() (Livewire 2). Beide setzen eine PHP-Property direkt vom JavaScript aus.

  • license_key: 'gpl' – Seit TinyMCE 7 (2024) ist die Community-Edition unter GPL lizenziert. Ohne diesen Key zeigt TinyMCE eine Warnmeldung. Für kommerzielle Projekte, die nicht GPL-kompatibel sind, brauchen Sie einen kostenpflichtigen API-Key.

Häufige Stolperfallen

Mehrere Editoren auf einer Seite: Der Selector '#editor' funktioniert nur für ein Element. Bei mehreren TinyMCE-Instanzen (z.B. in einer Livewire-Schleife) brauchen Sie einen dynamischen Selector wie '.tinymce-editor' und müssen jede Instanz einzeln mit tinymce.get() ansprechen.

Initiales Laden: Wenn $document serverseitig befüllt ist, muss der Inhalt als Value der Textarea gerendert werden ({{ $document }}), nicht per wire:model allein – denn Livewire hydratisiert erst nach dem initialen Render.

Filament-Alternative: Wer Filament nutzt, braucht TinyMCE in vielen Fällen nicht. Filaments eingebauter Rich-Text-Editor (basierend auf Tiptap) ist Livewire-nativ und hat das DOM-Problem nicht. TinyMCE lohnt sich, wenn Sie spezifische Features brauchen (Templates, Mail-Merge, erweiterte Tabellen), die Tiptap nicht bietet.

Das allgemeine Pattern

Die Kombination wire:ignore + manueller Event-Sync funktioniert für jede JavaScript-Bibliothek, die eigene DOM-Elemente erzeugt. Das Pattern ist immer gleich:

  1. Container mit wire:ignore umschließen

  2. JS-Bibliothek im @script-Block initialisieren

  3. Änderungen per $wire.set() zurück an Livewire synchronisieren

  4. Falls nötig: $wire.on() für serverseitige Updates, die den Editor-Inhalt ändern sollen

Dokumentation: Livewire wire:ignore | TinyMCE Laravel Integration