Skip to content

Instantly share code, notes, and snippets.

@ju5t
Last active May 3, 2024 21:45
Show Gist options
  • Save ju5t/579a90b03f5cb80f3c6984d48a3385c4 to your computer and use it in GitHub Desktop.
Save ju5t/579a90b03f5cb80f3c6984d48a3385c4 to your computer and use it in GitHub Desktop.
Livewire enabled TinyMCE blade component

Instructions

This is a very basic TinyMCE component. It uses 'entangle'. This allows you to link a Livewire and Alpine property to eachother. If one value changes, the other does too.

Installation

Add tinymce.blade.php to views/components/input. This can be another component folder too if you prefer, but keep in mind that you should also change the x-input.tinymce structure accordingly.

Usage

Make sure you have added the TinyMCE library in the layout that you're extending. Although with some extra work the component should be able to do this for you, it doesn't right now.

After the TinyMCE library has been added, this is how you use it:

<x-input.tinymce wire:model="editor" placeholder="Type anything you want..." />

Note that all HTML attributes will propogate to the text area. In this case the placeholder will automatically be passed on to the input in the blade component.

In the example we link it to editor. This can be any wired property in the Livewire component.

<div
x-data="{ value: @entangle($attributes->wire('model')) }"
x-init="
tinymce.init({
target: $refs.tinymce,
themes: 'modern',
height: 200,
menubar: false,
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste code help wordcount'
],
toolbar: 'undo redo | formatselect | ' +
'bold italic backcolor | alignleft aligncenter ' +
'alignright alignjustify | bullist numlist outdent indent | ' +
'removeformat | help',
setup: function(editor) {
editor.on('blur', function(e) {
value = editor.getContent()
})
editor.on('init', function (e) {
if (value != null) {
editor.setContent(value)
}
})
function putCursorToEnd() {
editor.selection.select(editor.getBody(), true);
editor.selection.collapse(false);
}
$watch('value', function (newValue) {
if (newValue !== editor.getContent()) {
editor.resetContent(newValue || '');
putCursorToEnd();
}
});
}
})
"
wire:ignore
>
<div>
<input
x-ref="tinymce"
type="textarea"
{{ $attributes->whereDoesntStartWith('wire:model') }}
>
</div>
</div>
@danabrey
Copy link

Thanks so much for putting this up!

Great help.

@dhruva81
Copy link

Thank you very much.

@jelmersdp
Copy link

Really useful, thank you!!

@pranayBaddam
Copy link

If we use livewire modal, it is working fine on first time, then when we close modal and open it again, tinymce editor is not showing.

I think, tinymce instance not destroyed when we close modal and It is trying to re-instantiate again when we open modal again. So I am removing tinymce instance and instantiating again. It is working for me. Hope It help others too.

<div>
    <div
        x-data="{ value: @entangle($attributes->wire('model')) }"
        x-init="
        tinyMCE.remove('#' + $refs.tinymce.id);
        tinyMCE.init({
            selector: '#' + $refs.tinymce.id,
            height: 200,
            menubar: true,
            plugins: [
                'advlist autolink lists link image charmap print preview anchor',
                'searchreplace visualblocks code fullscreen',
                'insertdatetime media table paste code help wordcount'
            ],
            toolbar: 'undo redo | formatselect | ' +
                'bold italic backcolor | alignleft aligncenter ' +
                'alignright alignjustify | bullist numlist outdent indent | ' +
                'removeformat | help',
            setup: function(editor) {
                editor.on('blur', function(e) {
                    value = editor.getContent()
                })
                editor.on('init', function (e) {
                    if (value != null) {
                        editor.setContent(value)
                    }
                })
                function putCursorToEnd() {
                    editor.selection.select(editor.getBody(), true);
                    editor.selection.collapse(false);
                }
                $watch('value', function (newValue) {
                    if (newValue !== editor.getContent()) {
                        editor.resetContent(newValue || '');
                        putCursorToEnd();
                    }
                });
            }
        })
    "
        wire:ignore
    >
        <div>
            <x-input.textarea
                x-ref="tinymce"
                {{ $attributes->whereDoesntStartWith('wire:model') }}
            ></x-input.textarea>
        </div>
    </div>
</div>

@ettiennelouw
Copy link

Perfect pranayBaddam had the same issue and working great now.

@boranbar
Copy link

Hello,

Thank you for this great component.

I have a weird problem.

In my multi-language form, this doesn't work with CREATE component.

On UPDATE, everything is fine.

On CREATE, i defined a collection which has dynamic fields depends on app locale.

public Collection $translatableFields;

Then in mount I'm assigning like this:

$this->translatableFields = $this->prepareDynamicFields();

public function prepareDynamicFields(): Collection
    {
        $dynamicFields = collect();
        foreach ($this->locales as $locale){
            
            $dynamicFields->push([
                "title.$locale" => null,
                "body.$locale" => null,
                "meta_title.$locale" => null,
                "meta_description.$locale" => null,
                "meta_keywords.$locale" => null,
            ]);
        }

        return $dynamicFields->collapse();
    }

In the livewire blade, I'm using like this;
<x-input.tinymce wire:model="translatableFields.body.{{ $locale }}" placeholder="Type anything you want..." />

Then I'm getting following errors on console:

Livewire Entangle Error:  Livewire property translatableFields.body.en can not be found.
.
.
.

For all the locales.

How can i fix this?

@ju5t
Copy link
Author

ju5t commented Dec 29, 2022

It says that the property translatableFields.body.en cannot be found. This isn't an issue with the TinyMCE code but apparently your locales are not always loading as you expect. I would check that.

@boranbar
Copy link

Yeah, the problem is entangle part.

x-data="{ value: @entangle($attributes->wire('model')) }"

I wanted to make it dynamic for my public properties based on language options.

So, I managed to find a workaround like this:

public Post $post;

public function mount()
    {
        $this->post = new Post();
        $this->post->body = [];
    }

After assigning $this->post->body to empty array, the entangle error gone and now working properly.

Thanks for the reply.

@sacalito
Copy link

Thankyou!

@checkmaldierethorik
Copy link

Thank you !

@Jagadish056
Copy link

Check it out!

Reusable Alpine Component
in resources\js\app.js

TinyMCE Integration for Livewire with Alpine.js and CSP
Here's a reusable TinyMCE integration for Livewire while keeping Alpine.js and Content Security Policy (CSP) in mind.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment