In case you are not familiar with the stack, this is a little tech reference:
- Livewire - Livewire is a full-stack framework for Laravel that takes the pain out of building dynamic UIs.
- Alpine.js - Alpine.js is a rugged, minimal framework for composing JavaScript behavior in your markup.
- Quill - Quill is a modern WYSIWYG editor built for compatibility and extensibility.
Using third party libraries in Livewire is a very common use case. This time I faced the need to add a WYSIWYG editor and wanted to show how it can be achieved.
The very short response is shown in this snippet. Next, I will break down every piece.
This is how your Livewire component will look like.
<!-- livewire/edit-component.blade.php -->
@push('styles')
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
@endpush
@push('scripts')
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
@endpush
<div class="mt-2 bg-white" wire:ignore>
<div
x-data
x-ref="quillEditor"
x-init="
quill = new Quill($refs.quillEditor, {theme: 'snow'});
quill.on('text-change', function () {
$dispatch('input', quill.root.innerHTML);
});
"
wire:model.debounce.2000ms="description"
>
{!! $description !!}
</div>
</div>
Now, let's break down the pieces that are working here.
In order to use Quill we need to import the library into our webpage. We use a CDN that will provide the css
and the js
that is required.
We are adding the <link>
and <script>
tags by using laravel blade stacks.
<!-- layouts/base.blade.php -->
<html>
<head>
@stack('styles')
</head>
<body>
@stack('scripts')
</body>
</html>
<!-- livewire/edit-component.blade.php -->
@push('styles')
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
@endpush
@push('scripts')
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
@endpush
Next, add a <div>
element in which we will be mounting Quill. Alpine.js will be used to mount the Editor into this element.
x-data
will tell Alpine.js to declare a new component scope in this element, this way we can use other Alpine.js features.
x-ref=quillEditor
will let us reference this DOM element in a very convenient way. We will be able to use $refs.quillEditor
later when we need to reference this element.
x-init=
will tell Apline.js to run an expression when this component is initialized. That is why we added the x-data
on that component.
At this point we can already use Quill, we will initialize it and add its event handler.
First, we create a new instance of Quill and use the $ref.quillEditor
to bind the editor into this Element.
Then, we use the same instance and add an event handler to it. The event handler will listen to the text-change
event. When this event happens, we will fetch the HTML that has been added to the editor and by using Alpine.js's $dispatch
feature, we will emit an input
event in this component.
<div
...
x-init="
quill = new Quill($refs.quillEditor, {theme: 'snow'});
quill.on('text-change', function () {
$dispatch('input', quill.root.innerHTML);
});
"
>...</div>
wire:model.debounce.3000ms="description"
. Given that we are now reproducing an input
event, we can use Livewires data binding feature. Adding
wire:model="description"` will bind the value of the Quill editor right into our Livewire component. This way we can have this data in our Laravel app.
Adding debounce.2000ms
to the data binding will debounce these many input
events and update the value of the property after 2000 ms.
wire:ignore
Adding this to a parent element will tell Livewire to skip DOM-diffing for this element, this is useful because Quill adds elements that Livewire does not know from the original HTML. This will prevent a small page flicker when re-rendering Livewire components.
Closing
That's it, very easy and right into your HTML Markup.
The same approach will work with many other third party libraries.
Enjoy the Livewire - AlpineJs Stack.
Top comments (10)
Thanks for this! If someone is already just using vanilla javascript with an input element changing value of it on('text-change'), they can use
inputElement.dispatchEvent(new Event('input'))
after changing the value to fix the problem withwire:model
.Undefined index: value
Does it work for you?
laravelplayground.com/#/snippets/4...
This is why it doesn't work correctly github.com/livewire/livewire/issue...
The problem is in the editor.
In the playground, the same error. I can't figure out why.
I think this is working now...
laravelplayground.com/#/snippets/0...
Let me know if this works so I can update the post
It definitely works. Could you tell me more about how to upload images using quill to the server. In this blog post?
In laravel, the issue of validation can be solved like: 'content' => [
'required',
function ($attribute, $value, $fail) {
if (!trim(strip_tags($value))) {
$fail('The content field is required.');
}
},
],
Super elegant solution. Livewire 3 I switched set for $wire and worked a treat.
Hi, I followed your tutorial and it was great! But is it also possible to auto-detect images and upload them using Livewire?