JiHong88 / suneditor

Pure javascript based WYSIWYG html editor, with no dependencies.
http://suneditor.com
MIT License
1.73k stars 309 forks source link

How to using in livewire #789

Open Saeeed-B opened 3 years ago

Saeeed-B commented 3 years ago

@JiHong88 hi. How to using with livewire framework? How to use wire:model? With Ex please.

JiHong88 commented 3 years ago

@Saeeed-B Sorry, I don't know well of the livewire framework. :(

kaju74 commented 2 years ago

Are u still interested in how to use SunEditor with Livewire & Alpine.JS? I've implemented this just a few days ago and it work's like a charme until now 8-) Could write a possible solution on interest...

JiHong88 commented 2 years ago

Absolutely! I think it would be very good.

kaju74 commented 2 years ago

Okay, here we go 8-)

In our Livewire component, we need a public variable to store the content. This could be a 'normal' string variable or an eloquent field from our model (e.g. public Post $post;). Make sure you include this field in the validation rules() of the livewire component to get the form attributes after submitting it (e.g. $attributes = $this->getValidatedAttributes()). Take a look at the livewire documentation if you're not familiar with this form processing stuff.

To be able to use the SunEditor in our form, the easiest way is to create a blade component for this. This component can then be used in our Livewire blade template as usual:

<x-suneditor wire:model.defer="post.description">
    My post content
</x-suneditor>

Use the "defer" directive to get rid of unneccessary server calls.

Now we take a look at our blade component. To be able to use multiple SunEditor's in one form, we first need to create unique names for all instances. The simpliest way is to include a small php call in our blade component for this:

@php
    $name = $attributes->wire('model')->value();
@endphp

The $name variable includes now the name of the linked wire model (i.e. "post.description") and can be used for the textarea and an optional html label tag:

<textarea x-data="editor($el, @entangle($attributes->wire('model')))" name="{{ $name }}" x-cloak></textarea>
<label for="{{ $name }}">
    {{ $slot }}:
</label>

The "$slot" will be replaced with our content above ("My post content") and is used for the textarea label. The textarea itself injects Alpine.js via the "x-data" directive and uses the "@entangle" keyword to 2-way-link Alpine with our Livewire model. The "x-cloak" custom html attribute is used to hide the textarea while loading the content. It's defined in the "app.css" file of our Laravel application:

/**
 * Alpine.js styling.
 */
[x-cloak] {
    visibility: hidden;
    overflow: hidden;
    display: none;
}

I always try to separate larger Alpine.js logic from my blade components, so I call the "editor" function which code is placed in it's own file. So let's take a look at the "app.js" file of our app first:

/**
 * Alpine.js library.
 *
 * You can find the documentation here:
 * https://github.com/alpinejs/alpine
 */
import Alpine from 'alpinejs';

// set alpine available via global window
window.Alpine = Alpine;

// inject custom components
require('./components/suneditor');

// start it!
Alpine.start();

We include the SunEditor functionality by 'require'-ing the separate Javascript file (suneditor.js) in the 'components'-subfolder:

import SunEditor from "suneditor";
import {align, blockquote, fontColor, hiliteColor, list, table, link} from 'suneditor/src/plugins'

document.addEventListener('alpine:init', () => {
    Alpine.data('editor', (el, model) => ({
        value: model,
        init() {
            let parent = this;

            let editor = SunEditor.create(el, {
                value: this.value,
                plugins: [align, blockquote, fontColor, hiliteColor, list, table, link],
                buttonList: [
                    ...
                ],
            });

            editor.onChange = function (contents) {
                parent.value = contents;
            }
        }
    }))
})

First, we need to make sure that Alpine.js has finished it's initialization stuff. Then we define our 'editor'-function with accepts just two parameters: "el" and "model". The "el" passes the textarea element from the dom to be used and the "model" holds the Livewire model defined. Then we create a new SunEditor instance with the plugins to use (and some other stuff you need). To fill the editor with the previously stored content, we only need to set the 'value' variable. To update the wire model, we hook into the "onChange" callback from SunEditor. At this point, we just fill the value with the new content - that's it.

Saeeed-B commented 2 years ago

i use like this:

 const editor = SUNEDITOR.create();
  editor.onChange = function (contents, core) {
      @this.set('Content', core);
  }

this is very simple and apline is not required