CK Editor Laravel Livewire - javascript

Is there anyway for Laravel Livewire to make my CKEditor the same behavior as a wire:model.lazy? currently I have a script tag that listens for any changes. Which causes for every type a request towards the component..
<script>
ClassicEditor
.create(document.querySelector('#body'))
.then(editor => {
editor.model.document.on('change:data', () => {
#this.set('body', editor.getData());
})
})
.catch(error => {
console.error(error);
});
</script>
The behavior I want is either a button or everytime I lose focus on the CKEditor the $body will be updated.

Just listen to the submit button and update the value on click:
let editor;
ClassicEditor.create(document.getElementById('post'), {
// configs
})
.then(newEditor => {
editor = newEditor;
})
.catch(error => {});
document.querySelector('button[type="submit"]').addEventListener('click', function () {
#this.set('post', editor.getData());
});

For me and anybody else who have the same issue
The main issue here is on.change this piece of code on( 'change:data'... will make the editor send post request on every single key press.
Solving the issue.
<script>
let body_changed = false;
ClassicEditor
.create(document.getElementById('body'), {})
.then(editor => {
window.body = editor;
detectTextChanges(editor);
detectFocusOut(editor);
})
function detectFocusOut(editor) {
editor.ui.focusTracker.on('change:isFocused', (evt, name, isFocused) => {
if (!isFocused && body_changed) {
body_changed = false;
#this.set('body', editor.getData());
}
})
}
function detectTextChanges(editor) {
editor.model.document.on('change:data', () => {
body_changed = true;
});
}
</script>
Hope this will help me and others in future :)

Related

Why is my Event Listener not Firing After the Second Event?

I'm building a photo upload form with Livewire, Alpine, and FilePond. After the photos have finished processing I want to show a "Save" button to persist the files.
I'm using Alpine to handle the show/hide. Both event listeners are working correctly when separated, but I just can't seem to get them to work together. The below code is not throwing errors, but it's also not showing the buttons inside the showSaveButtons div.
What am I doing wrong?
<div x-data="showSaveButtons">
<div x-show="open">
Buttons Go Here
</div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('showSaveButtons', () => ({
open: false,
}));
document.addEventListener("FilePond:processfiles", () => {
Alpine.data('showSaveButtons', () => ({
open: true,
}));
});
})
</script>
This is because you're creating a second Alpine.data object, instead of editing the existing one.
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('showSaveButtons', () => ({
open: false,
init() {
// So we can access "this" inside the listener
let context = this;
document.addEventListener("FilePond:processfiles", () => {
context.open = true;
});
}
});
});
</script>
If you're curious about the init function, check the docs

Drop down menu directing to same URL

I have created a drop down menu with multiple options to chose from.
However what ever option I chose it directs me to the same URL. In this case the last written code. Could someone please tell me what I have missed in the code?
import wixLocation from 'wix-location';
$w.onReady(function () {
$w("#dropdown1").onChange((event, $w) => {
console.log(event.target.value); //iPhone X
wixLocation.to("/iphonex");
});
});
$w.onReady(function () {
$w("#dropdown1").onChange((event, $w) => {
console.log(event.target.value); //iPhone XS
wixLocation.to("/iphonexs");
});
});
$w.onReady(function () {
$w("#dropdown1").onChange((event, $w) => {
console.log(event.target.value); //iPhone XS MAX
wixLocation.to("/iphonexsmax");
});
});
You only need one event handler. You need to go to the value
If you have more than one, the last one is executed
import wixLocation from 'wix-location';
$w.onReady(function () {
$w("#dropdown1").onChange((event, $w) => {
var page = event.target.value); //"iPhone X" OR "iPhone XS" etc
if (page) wixLocation.to("/"+page.replace(/\s+/g,"").toLowerCase());
});
});
If you instead set the VALUE of the options to iphonex, iphonexs etc, then you can just do
$w("#dropdown1").onChange((event, $w) => {
var page = event.target.value); // "iphonex" OR "iphonexs" etc
if (page) wixLocation.to("/"+page)
});

Cannot get instance of CKEditor

I have several fields which need to be initialized with CKEditor, for this I have created an helper class that contains the initEditor method.
The method below should return the initialized editor but it doesn't:
window.CKEditorHelper = window.CKEditorHelper || {};
(function (exports) {
exports.initEditor = function (input, myEditor) {
ClassicEditor
.create(document.querySelector(input), {
language: {
ui: 'en'
content: 'en'
}
})
.then(editor => {
myEditor = editor;
});
};
})(window.CKEditorHelper);
this is called in the following way:
let editor = null;
CKEditorHelper.initEditor('#description', editor);
so when I click on a button:
$('#save').on('click', function(){
console.log(editor.getData());
});
I get:
Cannot read property 'getData' of null
what I did wrong?
There are some issues on your code
let editor = null;
the let keyword only define a variable within function scope, when you use editor on another scope (your click handle event), it could be undefined
Another line
myEditor = editor;
This just simple made the reference to your original editor object will gone
Here is my solution to fix it
Change the way you init an editor like bellow
window.editorInstance = {editor: null};
CKEditorHelper.initEditor('#description', editorInstance);
Change your CKEditorHelper to
window.CKEditorHelper = window.CKEditorHelper || {};
(function (exports) {
exports.initEditor = function (input, myEditorInstance) {
ClassicEditor
.create(document.querySelector(input), {
language: {
ui: 'en'
content: 'en'
}
})
.then(editor => {
myEditorInstance.editor = editor;
});
};
})(window.CKEditorHelper);
And when you want to use your editor
console.log(editorInstance.editor.getData());
You can give this in javascript
$(document).ready(function () {
CKEDITOR.replace('tmpcontent', { height: '100px' })
})
take the value by using following
$('#save').on('click', function(){
var textareaValue = CKEDITOR.instances.tmpcontent.getData();
});
<label class="control-label">Message</label>
<textarea name="tmpcontent" id="tmpcontent" class="form-control"></textarea>
//OR in latest version
var myEditor;
ClassicEditor
.create( document.querySelector( '#description' ) )
.then( editor => {
console.log( 'Editor was initialized', editor );
myEditor = editor;
} )
.catch( err => {
console.error( err.stack );
} );
and then get data using
myEditor.getData();

how to create alert confirm box in vue

i want to show a dialog box before deleting a files, how i can do it with vue?
here what i try
my button to delete a file
Delete
and here my delete method
DeleteUser(id, index) {
axios.delete('/api/artist/'+id)
.then(resp => {
this.artists.data.splice(index, 1);
})
.catch(error => {
console.log(error);
})
},
the dialog is showing but whatever i choose it keep delete the file.
Try this
Delete
DeleteUser(id, index) {
if(confirm("Do you really want to delete?")){
axios.delete('/api/artist/'+id)
.then(resp => {
this.artists.data.splice(index, 1);
})
.catch(error => {
console.log(error);
})
}
},
Simply use if(confirm('are you sure?')) inside DeleteUser.
DeleteUser(id, index) {
if(confirm('are you sure?'))
axios.delete('/api/artist/'+id)
.then(resp => {
this.artists.data.splice(index, 1);
})
.catch(error => {
console.log(error);
})
},
and remove the onClick
Demo https://jsfiddle.net/jacobgoh101/ho86n3mu/4/
You can install an alert library (Simple Alert) from here for more beautiful view
And then, you can use options to show alert box with question or anything else. This library has a few option. You can see them if you check above link.
this.$confirm("Are you sure ?", "Class is deleting...", "question").then(()=>{
axios.delete("/classes/" + studentClass.id).then(response =>{
this.$alert(response.data.message, 'Succes', 'success');
}).catch(error => {
this.$alert(error.response.data.message, 'Hata', 'error');
});
});
Description of alert box with parameters :
this.$confirm('Message is here', "Title is here", "type is here like success, question, warning");
For the case of using the Quasar Framework, you can use this plugin. I use globally !
export default {
methods: {
confirm() {
this.$q.dialog({
title: 'Confirm',
message: 'Would you like to turn on the wifi?',
cancel: true,
persistent: true
}).onOk(() => {
// console.log('>>>> OK')
}).onOk(() => {
// console.log('>>>> second OK catcher')
}).onCancel(() => {
// console.log('>>>> Cancel')
}).onDismiss(() => {
// console.log('I am triggered on both OK and Cancel')
})
}
}
}
<q-btn label="Prompt" color="primary" #click="prompt" />
Quasar Dialog

CKEditor5 Custom Modal Plugin

I followed the initial plugin tutorial and got the Image Insert to work, but I would like to display a custom modal with two input fields instead of the prompt to set some more attributes.
How would I best implement this?
I know how to implement a normal modal in plain JS/CSS but I am a bit confused as to where to put the HTML for the modal to be displayed on the button click.
class Test extends Plugin {
init() {
editor = this.editor
editor.ui.componentFactory.add('SampleView', (locale) => {
const view = new ButtonView(locale)
view.set({
label: 'test',
icon: imageIcon,
tooltip: true
})
view.on('execute', () => {
//here I would like to open the modal instead of the prompt
})
})
}
}
For example, you can try to use SweetAlert2 which is zero-dependency pure javascript replacement for default popups.
import swal from 'sweetalert2';
...
view.on( 'execute', () => {
swal( {
input: 'text',
inputPlaceholder: 'Your image URL'
} )
.then ( result => {
editor.model.change( writer => {
const imageElement = writer.createElement( 'image', {
src: result.value
} );
editor.model.insertContent( imageElement, editor.model.document.selection );
} );
} )
} );

Categories

Resources