quill
quill copied to clipboard
Extending default Quill handler for image to upload base64 data.
I am using the lib for a mobile app and I will love to be able to extend the current functionality of the image handler to grab the image data and post it before embedding it to the editor and I was wondering if this was possible and if I could get some help in implementing this.
- hit the image icon
- quill handler runs file dialogue
- user clicks on file
- file datauri is returned by quill but the I convert that to a file and upload and use the returned link instead of base64 obj.
Thanks.
Yes! I am also trying to do this exact same thing. We are storing the contents in a SQL database and do not want to store the base64 data of any images.
You can try something like this to overwrite the default image insert functionality and insert your own instead. @jwstevensii
this.editor = new Quill('#editor', {
modules: { toolbar: "#toolbar"},
placeholder: 'you can start here and make it a blast...',
theme: 'snow'
});
this.editor.getModule("toolbar").addHandler("image", imgHandler);
So how does this image uploading works? I just cannot find good example here. Is it possible to add button to toolbar which will open my custom gallery modal where I can select one of my uploaded images or upload new and it will return image url back to editor?
I met exactly same problem, don't know how to solve it.
The imgHandler
in the example is a function that gets passed a parameter, you could literally rewrite it as
function(a){
console.log(a);
// code that uploads your image and slots the resulting url into the editor goes here...
}
Try running tests to see what gets returned by the quill editor.
https://github.com/quilljs/quill/issues/863#issuecomment-240579430 This is very nice example, which helped me solved this problem. It is very easy to implement.
I finally solved the problem by using Ajax to upload image to server. Here are some links that I find useful (if you want to using ajax to implement this upload function, too). How can I upload files asynchronously? Ajax POST request via jQuery and FormData - $_POST empty on PHP Suggestions above are useful when you embed the upload function in your quill editor.
I solved the problem that upload image with url. This is my code, hope help you:
const editor = new Quill('#quill-editor', {
bounds: '#quill-editor',
modules: {
toolbar: this.toolbarOptions
},
placeholder: 'Free Write...',
theme: 'snow'
});
/**
* Step1. select local image
*
*/
function selectLocalImage() {
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.click();
// Listen upload local image and save to server
input.onchange = () => {
const file = input.files[0];
// file type is only image.
if (/^image\//.test(file.type)) {
saveToServer(file);
} else {
console.warn('You could only upload images.');
}
};
}
/**
* Step2. save to server
*
* @param {File} file
*/
function saveToServer(file: File) {
const fd = new FormData();
fd.append('image', file);
const xhr = new XMLHttpRequest();
xhr.open('POST', 'http://localhost:3000/upload/image', true);
xhr.onload = () => {
if (xhr.status === 200) {
// this is callback data: url
const url = JSON.parse(xhr.responseText).data;
insertToEditor(url);
}
};
xhr.send(fd);
}
/**
* Step3. insert image url to rich editor.
*
* @param {string} url
*/
function insertToEditor(url: string) {
// push image url to rich editor.
const range = editor.getSelection();
editor.insertEmbed(range.index, 'image', `http://localhost:9000${url}`);
}
// quill editor add image handler
editor.getModule('toolbar').addHandler('image', () => {
selectLocalImage();
});
Quill is awesome and easy to extensible.
@TaylorPzreal - I get the missing ) after formal parameters
error on the saveToServer function, which prevent the editor from loading altogether. Any ideas?
@TaylorPzreal thank you.
@TaylorPzreal Don't forget to implement by ourselves the ways to manage the images uploaded.
@TaylorPzreal I would add one line:
input.setAttribute('accept', 'image/*')
are u guys talking about angular 6, iam using it in angular 6, cannot figure it out
I solved the problem that upload image with url. This is my code, hope help you:
const editor = new Quill('#quill-editor', { bounds: '#quill-editor', modules: { toolbar: this.toolbarOptions }, placeholder: 'Free Write...', theme: 'snow' }); /** * Step1. select local image * */ function selectLocalImage() { const input = document.createElement('input'); input.setAttribute('type', 'file'); input.click(); // Listen upload local image and save to server input.onchange = () => { const file = input.files[0]; // file type is only image. if (/^image\//.test(file.type)) { saveToServer(file); } else { console.warn('You could only upload images.'); } }; } /** * Step2. save to server * * @param {File} file */ function saveToServer(file: File) { const fd = new FormData(); fd.append('image', file); const xhr = new XMLHttpRequest(); xhr.open('POST', 'http://localhost:3000/upload/image', true); xhr.onload = () => { if (xhr.status === 200) { // this is callback data: url const url = JSON.parse(xhr.responseText).data; insertToEditor(url); } }; xhr.send(fd); } /** * Step3. insert image url to rich editor. * * @param {string} url */ function insertToEditor(url: string) { // push image url to rich editor. const range = editor.getSelection(); editor.insertEmbed(range.index, 'image', `http://localhost:9000${url}`); } // quill editor add image handler editor.getModule('toolbar').addHandler('image', () => { selectLocalImage(); });
Quill is awesome and easy to extensible.
Your answer is very helpful, but how can I detect which image is being deleted by the user in the editor, As I want to delete the image from my server when the user deletes it
const editor = new Quill('#quill-editor', { bounds: '#quill-editor', modules: { toolbar: this.toolbarOptions }, placeholder: 'Free Write...', theme: 'snow' });
/**
* Step1. select local image
*
*/
function selectLocalImage() {
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.click();
// Listen upload local image and save to server
input.onchange = () => {
const file = input.files[0];
// file type is only image.
if (/^image\//.test(file.type)) {
saveToServer(file);
} else {
console.warn('You could only upload images.');
}
};
}
/**
* Step2. save to server
*
* @param {File} file
*/
function saveToServer(file: File) {
const fd = new FormData();
fd.append('image', file);
const xhr = new XMLHttpRequest();
xhr.open('POST', 'http://localhost:3000/upload/image', true);
xhr.onload = () => {
if (xhr.status === 200) {
// this is callback data: url
const url = JSON.parse(xhr.responseText).data;
insertToEditor(url);
}
};
xhr.send(fd);
}
/**
* Step3. insert image url to rich editor.
*
* @param {string} url
*/
function insertToEditor(url: string) {
// push image url to rich editor.
const range = editor.getSelection();
editor.insertEmbed(range.index, 'image', `http://localhost:9000${url}`);
}
// quill editor add image handler
editor.getModule('toolbar').addHandler('image', () => {
selectLocalImage();
});
Your answer is very helpful, but how can I detect which image is being deleted by the user in the editor, As I want to delete the image from my server when the user deletes it
I am not sure why anyone haven't talked about it? If we are working on uploading images to server, we need to know how we can reverse it, I mean delete it just by backspacing the image in the editor. I have tried to read through onContentChange/text-change etc. But can't find a proper way to do it. Have anyone done it, please share your here.
So, I have done so far is this to delete the uploaded image >
this.editorInstance.on('text-change', function(delta, oldDelta, source) {
if (source == 'api') {
console.log("An API call triggered this change.");
console.log(delta);
console.log(oldDelta);
} else if (source == 'user') {
console.log("A user action triggered this change."); // when a user is deleting the image from editor
console.log(delta);
console.log(oldDelta.ops[0].insert.image); // So you will get the link of the image
}
});
I solved the problem that upload image with url. This is my code, hope help you:
const editor = new Quill('#quill-editor', { bounds: '#quill-editor', modules: { toolbar: this.toolbarOptions }, placeholder: 'Free Write...', theme: 'snow' }); /** * Step1. select local image * */ function selectLocalImage() { const input = document.createElement('input'); input.setAttribute('type', 'file'); input.click(); // Listen upload local image and save to server input.onchange = () => { const file = input.files[0]; // file type is only image. if (/^image\//.test(file.type)) { saveToServer(file); } else { console.warn('You could only upload images.'); } }; } /** * Step2. save to server * * @param {File} file */ function saveToServer(file: File) { const fd = new FormData(); fd.append('image', file); const xhr = new XMLHttpRequest(); xhr.open('POST', 'http://localhost:3000/upload/image', true); xhr.onload = () => { if (xhr.status === 200) { // this is callback data: url const url = JSON.parse(xhr.responseText).data; insertToEditor(url); } }; xhr.send(fd); } /** * Step3. insert image url to rich editor. * * @param {string} url */ function insertToEditor(url: string) { // push image url to rich editor. const range = editor.getSelection(); editor.insertEmbed(range.index, 'image', `http://localhost:9000${url}`); } // quill editor add image handler editor.getModule('toolbar').addHandler('image', () => { selectLocalImage(); });
Quill is awesome and easy to extensible.
I am getting a missing ) error at saveToServer function, Kindly help I am in trouble figuring out, I am using javascript
@TaylorPzreal - I get the
missing ) after formal parameters
error on the saveToServer function, which prevent the editor from loading altogether. Any ideas?
Same here, do you get the solution if so then please share it with me..
Hi, I took the help from this discussion but I am facing an issue after uploading the image, I need to press enter event. Then only, image Url is being inserted into the editor content. Is there a way to do it while uploading the image?
@Utkarsh3587 Yes, I am having this same issue, it's a really annoying issue for content creators, is there any solution to that ? Please help needed from devs.
After inserting embed, somehow the selection should be at the end of the inserted thumbnail image to insert the url link in behind.
@TaylorPzreal - I get the
missing ) after formal parameters
error on the saveToServer function, which prevent the editor from loading altogether. Any ideas?Same here, do you get the solution if so then please share it with me..
I tried this code on the quill Interactive Playground just to check for invalid syntax, and I found that some function arguments have an invalid type specification. On the definition of the function "saveToServer" and "insertToEditor" try removing the argument type (": string", ": File"). After doing this I had no errors showing up on console and the editor showed up. Actually, it gave me a different error, but this is still invalid js syntax
@TaylorPzreal Its so useful Lots of thanks
For those interested in selecting and uploading multiple images at once (modified the code provided by @TaylorPzreal):
function selectLocalImage() {
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('multiple', 'multiple');
input.setAttribute('accept', 'image/png, image/gif, image/jpeg, image/webp')
input.click();
// Listen upload local image and save to server
input.onchange = () => {
const fileList = Array.from(input.files);
saveToServer(fileList);
}
}
function saveToServer(files) {
const formData = new FormData();
files.forEach(file => formData.append('images[]', file));
const xhr = new XMLHttpRequest();
xhr.open('POST', '//' + window.location.host + '/upload/image', true);
xhr.onload = () => {
if (xhr.status === 200) {
const data = JSON.parse(xhr.responseText).data;
data.forEach(url => insertToEditor(url));
}
};
xhr.send(formData);
}
function insertToEditor(url) {
// push image url to rich editor.
const range = quill.getSelection();
quill.insertEmbed(range.index, 'image', url);
}
Backend should return image urls like this:
{"data":["https://cdn.example.org/793de663-e648-462d-bd80-5697c920c63f.png"]}
Complete working Quill Editor with Uploading Image
import PropTypes from "prop-types";
import axios from "axios";
import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css";
import Quill from "quill";
// eslint-disable-next-line
class CustomToolbar extends React.Component {
constructor() {
super();
this.imageHandler = this.imageHandler.bind(this);
}
imageHandler() {
const input = document.createElement("input");
input.setAttribute("type", "file");
input.setAttribute("accept", "image/*");
input.click();
// eslint-disable-next-line
// eslint-disable-next-line
input.onchange = async function () {
const file = input.files[0];
const fd = new FormData();
fd.append("file", file);
const response = await axios.post(
`${process.env.REACT_APP_API_URL}/upload`,
fd
);
// return object with url property from server
const { url } = response.data;
const range = this.quill.getSelection();
this.quill.insertEmbed(range.index, "image", url);
}.bind(this);
}
render() {
return (
<div id="toolbar">
<select className="ql-header" defaultValue="">
<option value="1" />
<option value="2" />
<option value="" />
</select>
{/* eslint-disable-next-line */}
<button className="ql-bold" />
{/* eslint-disable-next-line */}
<button className="ql-italic" />
{/* eslint-disable-next-line */}
<button className="ql-underline" />
{/* eslint-disable-next-line */}
<button className="ql-strike" />
{/* eslint-disable-next-line */}
<button className="ql-blockquote" />
{/* eslint-disable-next-line */}
<button className="ql-code" />
{/* eslint-disable-next-line */}
<button className="ql-align" />
{/* eslint-disable-next-line */}
<button className="ql-link" />
{/* eslint-disable-next-line */}
<select className="ql-color" />
{/* eslint-disable-next-line */}
<select className="ql-background" />
{/* eslint-disable-next-line */}
<button className="ql-image" onClick={this.imageHandler} />
</div>
);
}
}
// eslint-disable-next-line
Quill.register("modules/imageUploader", function (quill, options) {
// eslint-disable-next-line
quill.getModule("toolbar").addHandler("image", function () {
const input = document.createElement("input");
input.setAttribute("type", "file");
input.setAttribute("accept", "image/*");
input.click();
// eslint-disable-next-line
input.onchange = async function () {
const file = input.files[0];
const fd = new FormData();
fd.append("file", file);
const response = await axios.post(
`${process.env.REACT_APP_API_URL}/upload`,
fd
);
const { url } = response.data;
const range = quill.getSelection();
quill.insertEmbed(range.index, "image", url);
};
});
});
QuillEditor.propTypes = {
id: PropTypes.string,
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
error: PropTypes.bool,
};
// eslint-disable-next-line
export default function QuillEditor({
id,
error,
value,
onChange,
simple = false,
sx,
...other
}) {
return (
<>
<CustomToolbar />
<ReactQuill
value={value}
onChange={onChange}
modules={{
toolbar: {
container: "#toolbar",
},
imageUploader: {},
}}
placeholder="Write something awesome..."
theme="snow"
{...other}
/>
</>
);
}
Quill 2.0 has been released (announcement post) with many changes and fixes. If this is still an issue please create a new issue after reviewing our updated Contributing guide :pray: