I am using CKeditor in react. On Form submit, why entered data is not coming.
Form submit, CKeditor data is still showing empty
How to get the CKEditor data both in text as well as HTML format.
Please find my code:
import React from "react";
import { CKEditor } from '#ckeditor/ckeditor5-react';
import ClassicEditor from '#ckeditor/ckeditor5-build-classic';
export class UseEditor extends React.Component {
constructor(props)
super(props);
this.state = {
DescriptionTextPlain: "",
DescriptionTextHTML: "",
loading: true,
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleEditorChange(event, editor) {
console.log({ event, editor });
}
handleSubmit(event) {
const DescriptionTextPlain = this.state.DescriptionTextPlain;
const DescriptionTextHTML = this.state.DescriptionTextHTML;
const data = {
DescriptionTextPlain, DescriptionTextHTML
}
fetch(REQUEST_URL, {
method: 'POST'
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<div>
<CKEditor name="DescriptionTextPlain" editor={ClassicEditor}
config={{
toolbar: [
'|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', '|',
'outdent', 'indent', '|', 'insertTable', 'undo', 'redo', 'style',
'strikethrough', 'subscript', 'superscript'
] }}
data={this.state.DescriptionTextPlain || ''}
onReady={editor => { this.editor = editor;}}
onChange={this.handleEditorChange}
Width="100%" Height="125px"
value={this.state.DescriptionTextPlain} />
</div> <input type="submit" name="submit" value="Submit" />
</form>
</div>
);
}
}
export default UseEditor;
Related
I'm using 'react-quilljs' package, and I want to add Tweet HTML Block to the toolbar, I couldn't find any tutorial how to in their webpage
The Code I'm Using:
import { useQuill } from 'react-quilljs';
import 'quill/dist/quill.snow.css';
export default function NewPost() {
const [content, setContent] = useState("");
//Quill Editor Toolbar
const modules = {
toolbar:{
container: [
[{'header': '1'}, {'header':'2'}, {'font':'[]'}],
[{size:[]}],
['bold', 'italic', 'underline', 'strike', 'blockquote'],
[{'list': 'ordered'}, {'list': 'bullet'}, {'indent':'-1'}, {'indent':'+1'}],
['link', 'image', imageHandler],
['clean'], ['code-block']
],
},
clipboard: {
matchVisual: false,
},
};
const { quill, quillRef } = useQuill();
useEffect(() => {
if (quill){
quill.on('text-change', () => {
console.log(quillRef.current.firstChild.innerHTML);
setContent(quillRef.current.firstChild.innerHTML)
});
}
}, [quill]);
console.log(content, "this is quill editor")
return (
<div className="newPostContentContainer">
<label className="newPostFormLabel">Post Content:</label
<div className="newPostContentTextareaContainer">
<div dir="auto" style={{ height: '300px', width: '100%', boxSizing: 'border-box'}}>
<div ref={quillRef} />
</div>
</div>
</div>
)
}
the problem is that the toolbar I have right now don't have the tweet HTML block or code sample, I have to add them by myself, which I failed to know how, any help would be appreciated.
Hello peepz of the web,
I've ran into mysterious corridor, which's too dark for me to see what the hell I'm going.. would love someone's flashlight to shine the way.
I have created a simple, poor to do list program.
It's combined from Task.js, TaskList.js and NewTaskForm.js.
From the NewTaskForm.js I'm retrieving input, passing it to the parent, TaskList.js.
On TaskList.js I'm adding a key to the task object:
handleSubmit(task2add){
let keyid = v4();
console.log(keyid);
let task2addWithID = { ...task2add, id: {keyid}, key: {keyid}};
this.setState(st => ({
todoArr: [...st.todoArr, task2addWithID],
}));
}
Now, in TaskList.js, in my render function, I'm creating Tasks:
render() {
let tasklist = this.state.todoArr.map(task => (
<div>
<Task
taskTitle={task.title}
taskText={task.text}
key={task.key}
id={task.id}
handleRemove={this.handleRemove}
handleEdit={this.handleEdit}
/>
</div>
));
return (
<div>
<h1>TaskList</h1>
<div className='TaskList'>
<div className='TaskList-title'>
{tasklist}
</div>
<NewTaskForm key={1} handleSubmit={this.handleSubmit}/>
</div>
</div>
)
}
now, what is so confusing for me is why when I'm doing in my Tasks.js class:
console.log(this.props.id);
it prints me an object?
I would expect it to.. print me a value? where along the way did it wrap it with an object?
Full (shitty) code below.
Plus #1, if anyone knows to tell me why still I get the warning the key, even though, at least for my poor experience, I have given it a key?
Plus #2, why even when I send the handleRemove function in TaskList.js the proper id, it doesn't erase the bloody task? :-(
Regards!
Task.js
import React, { Component } from 'react'
import './Task.css';
export default class Task extends Component {
constructor(props){
super(props);
this.handleRemove = this.handleRemove.bind(this);
}
handleRemove(evt){
evt.preventDefault();
console.log("MY ID MANNN");
console.log(this.props.id);
this.props.handleRemove(this.props.id.keyid);
console.log("CALLED");
}
render() {
return (
<div className='Task'>
<div className='Task-title'>
{this.props.taskTitle}
</div>
<div className='Task-text'>
{this.props.taskText}
</div>
<div>
<button className='Task-buttons'>Edit</button>
<button className='Task-buttons' onClick={this.handleRemove}>Delete</button>
</div>
</div>
)
}
}
NewTaskForm.js:
import React, { Component } from 'react';
import {v4} from 'uuid';
export default class NewTaskForm extends Component {
constructor(props){
super(props);
this.state = {
title: "", text: "", editing: false
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
// this.handleRemove = this.handleRemove.bind(this);
}
handleSubmit(evt){
evt.preventDefault();
// let stateWithID = { ...this.state, id: useId()};
this.props.handleSubmit(this.state);
this.setState({
title: "",
text: ""
})
}
handleChange(evt){
evt.preventDefault();
this.setState({
[evt.target.name]: evt.target.value
});
}
render() {
return (
<div>
<h2>Insert new Task:</h2>
<form onSubmit={this.handleSubmit}>
<label htmlFor="title">Title</label>
<input
name='title'
id='title'
type='text'
onChange={this.handleChange}
value={this.state.title}
/>
<div>
<label htmlFor="text">text</label>
<input
name='text'
id='text'
type='text'
onChange={this.handleChange}
value={this.state.text}
/>
</div>
<button>Submit</button>
</form>
</div>
)
}
}
TaskList.js
import React, { Component } from 'react'
import NewTaskForm from './NewTaskForm';
import Task from './Task';
import './TaskList.css';
import {v4} from 'uuid';
export default class TaskList extends Component {
constructor(props){
super(props);
this.state = {
todoArr: [
// {title: "test", text: "this shit", key: "", id: ""},
// {title: "test2", text: "this shi2", key: "", id: ""},
// {title: "test3", text: "this shi3", key: "", id: ""}
],
isEditing: false,
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handleRemove = this.handleRemove.bind(this);
this.handleEdit = this.handleEdit.bind(this);
}
handleSubmit(task2add){
let keyid = v4();
console.log(keyid);
let task2addWithID = { ...task2add, id: {keyid}, key: {keyid}};
this.setState(st => ({
todoArr: [...st.todoArr, task2addWithID],
}));
}
handleRemove(keyid){
console.log("IN HANDLE REMOVE");
console.log(keyid);
this.setState(st => ({
todoArr: st.todoArr.filter(n => n.keyid !== keyid )
}));
}
handleEdit(id){
}
render() {
let tasklist = this.state.todoArr.map(task => (
<div>
<Task
taskTitle={task.title}
taskText={task.text}
key={task.key}
id={task.id}
handleRemove={this.handleRemove}
handleEdit={this.handleEdit}
/>
</div>
));
return (
<div>
<h1>TaskList</h1>
<div className='TaskList'>
<div className='TaskList-title'>
{tasklist}
</div>
<NewTaskForm key={1} handleSubmit={this.handleSubmit}/>
</div>
</div>
)
}
}
Because you have created an object here:
{ ...task2add, id: {keyid}, key: {keyid}};
{keyid}
is the same as
{keyid: keyid}
You wanted to do this:
{ ...task2add, id: keyid, key: keyid};
I'm assuming its because you're setting the state like this:
let task2addWithID = { ...task2add, id: {keyid}, key: {keyid}};
The id attribute is assigned an object. If you did:
let task2addWithID = { ...task2add, id: keyid, key: {keyid}};
console logging id should print out a string
I am using react-quill 1.3.3. I also allow for the addition of images into the posts. The way that quill works right now, is that it just converts the image to a string and sends it all as text. The problem is that this seems to take a lot of extra memory (data). For instance, I am getting 413 errors, "413 (Payload Too Large)", for images that are only 20K, etc. The real way to do this is to convert your images into links and save the images to a cloud service. I already have the cloud service working for saving avatar images, so that can just be copied. The problem is, how do we get react-quill to do this? Quill seems like a one stop shop that just converts your images to text, so we are going to have to do some special coding and I have no idea how to do that.
Here is the quill component:
import React from 'react'
export default class PostEditor extends React.Component {
constructor(props) {
super(props)
this.state = { editorHtml: '', theme: 'snow' }
this.handleChange = this.handleChange.bind(this)
if (typeof window !== 'undefined') {
this.ReactQuill = require('react-quill');
require('katex');
require('react-quill/dist/quill.snow.css');
}
}
handleChange (value) {
this.props.onChange(value);
}
render() {
const ReactQuill = this.ReactQuill
const { value } = this.props;
if (typeof window !== 'undefined' && ReactQuill) {
return (
<ReactQuill
onChange={this.handleChange}
theme="snow"
value={value}
modules={PostEditor.modules}
/>
)
} else {
return <textarea />;
}
}
}
PostEditor.modules = {
toolbar: [
[{ 'header': '1'}, {'header': '2'}, { 'font': [] }],
[{size: []}],
['bold', 'italic', 'underline', 'strike', 'blockquote'],
[{'list': 'ordered'}, {'list': 'bullet'},
{'indent': '-1'}, {'indent': '+1'}],
['link', 'image', 'video','formula'],
['clean']
],
clipboard: {
// toggle to add extra line breaks when pasting HTML:
matchVisual: false,
}
}
PostEditor.formats = [
'header', 'font', 'size',
'bold', 'italic', 'underline', 'strike', 'blockquote',
'list', 'bullet', 'indent',
'link', 'image', 'video', 'formula'
]
// PostEditor.propTypes = {
// placeholder: PropTypes.string,
// }
Here is some work done to do something similar, but I have no idea how to add this code to my app.
What I am expecting is that we should be able to use the "add image" button on the quill toolbar and the wysiwyg works fine and the image is taken from the user's hard drive and placed in the text properly formated. The difference is that when you save, the image is sent to your cloud storage and a link is pasted into the saved html. When you view the post, that link is expanded into an image again.
If anyone knows why the images are being converted into such large sizes (more than 20MB) please let me know because I might also just accept that as a solution.
Here is the code for the part of the component that contains the quill:
{this.isActiveField('postText') && (
<Fieldset className="mt2">
<PostEditor
id="postText"
onChange={this.onChange}
value={postText}
/>
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
open={!!ErrorHandling.getFieldErrors(errors, 'postText')}
autoHideDuration={4000}
onClose={this.handleClose}
ContentProps={{
'aria-describedby': 'message-id',
}}
message={<span id="message-id">{ErrorHandling.getFieldErrors(errors, 'postText')}</span>}
action={[
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={this.handleClose}
>
<CloseIcon />
</IconButton>,
]}
/>
</Fieldset>
)}
Edit:
I worked out the second solution. In my Apollo server code I added the bodyParser as recommended here. It seems to work fine now.
I have a form and I replaced the textarea with ReactQuill based on this tutorial (https://www.youtube.com/watch?v=DjEANvaZFv0&feature=youtu.be) to get Rich Text. However once I did it, I got an error saying 'Error: React.Children.only expected to receive a single React element child' (see screenshot below).
This only came up after I replaced the textarea with ReactQuill but on the error page it shows me the code in the App.js where I've implemented google authentication with firebase and I'm not sure how the two are connected. How do I fix this?
Here's my AddArticle.js where the form is:
import React, { Component } from "react";
import firebase from "../Firebase";
import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css";
import renderHTML from 'react-render-html';
class AddArticle extends Component {
constructor() {
super();
this.ref = firebase.firestore().collection("articles");
this.state = {
title: "",
content: "",
};
}
onChange = (e) => {
const state = this.state;
state[e.target.name] = e.target.value;
this.setState(state);
};
onBodyChange(e) {
this.setState({ content: e });
}
onSubmit = (e) => {
e.preventDefault();
const { title, content } = this.state;
this.ref
.add({
title,
content,
})
.then((docRef) => {
this.setState({
title: "",
content: "",
});
this.props.history.push("/");
})
.catch((error) => {
console.error("Error adding document: ", error);
});
};
render() {
const { title, content } = this.state;
return (
<div className="container">
<br></br>
<br></br>
<br></br>
<div className="panel panel-default">
<div className="panel-heading">
<h3 className="panel-title text-center">Create a new article</h3>
</div>
<br></br>
<br></br>
<div className="panel-body">
<form onSubmit={this.onSubmit}>
<div className="form-group input-group-lg">
<label for="title">Title:</label>
<input
type="text"
className="form-control"
name="title"
value={title}
onChange={this.onChange}
placeholder="Title"
/>
</div>
<div className="form-group">
<label for="content">Content:</label>
<ReactQuill
modules={AddArticle.modules}
formats={AddArticle.formats}
name="content"
onChange={this.onBodyChange}
placeholder="Content"
cols="80"
rows="20"
>
{content}
</ReactQuill>
</div>
<button type="submit" className="btn btn-success">
Submit
</button>
</form>
</div>
</div>
</div>
);
}
}
// Quill Config
AddArticle.modules = {
toolbar: [
[{ header: [1, 2, 3, 4, 5, 6, false] }],
[{ size: [] }],
["bold", "italic", "underline", "strike", "blockquote"],
[
{ list: "ordered" },
{ list: "bullet" },
{ indent: "-1" },
{ indent: "+1" },
],
["link", "image", "video"],
["clean"],
["code-block"],
],
clipboard: {
// toggle to add extra line breaks when pasting HTML:
matchVisual: false,
},
};
AddArticle.formats = [
"header",
"font",
"size",
"bold",
"italic",
"underline",
"strike",
"blockquote",
"list",
"bullet",
"indent",
"link",
"image",
"video",
"code-block",
];
export default AddArticle;
And here is my App.js in case it's relevant:
import React, { Component } from "react";
import "./App.css";
import Navbar from "./components/Navbar";
import Main from "./Main";
import firebase from "firebase";
import StyledFirebaseAuth from "react-firebaseui/StyledFirebaseAuth";
class App extends Component {
state={isSignedIn:false}
uiConfig = {
signInFlow: "popup",
signInOptions: [
firebase.auth.GoogleAuthProvider.PROVIDER_ID
],
callbacks: {
signInSuccess: () => false
}
}
componentDidMount = () => {
firebase.auth().onAuthStateChanged(user => {
this.setState({isSignedIn:!!user})
})
}
render() {
return (
<div>
{this.state.isSignedIn ? (
<span>
<Navbar />
<Main />
</span>
) :
(
<StyledFirebaseAuth
uiConfig={this.uiConfig}
firebaseAuth={firebase.auth()}
/>
)}
</div>
);
}
}
export default App;
As described here, I suggest passing content as ReactQuill's value instead of making it a child:
<ReactQuill
value={this.state.content}
// ... other props are OK
/> // Close the tag: no children
I need to add a custom toolbar button inside the react quill HTML editor.
I have followed the tutorial https://github.com/zenoamaro/react-quill#custom-toolbar but was not able to get this to work. The code is as follows.
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import ReactQuill from 'react-quill'; // ES6
import './style.scss';
class DocumentForm extends Component {
constructor(props) {
super(props);
this.state = {
file: null,
documentDetails: {},
text: ''
};
this.handleEditorChange = this.handleEditorChange.bind(this);
this.modules = {
toolbar: [
[{ 'header': [1, 2, false] }],
['bold', 'italic', 'underline','strike', 'blockquote'],
[{'list': 'ordered'}, {'list': 'bullet'}, {'indent': '-1'}, {'indent': '+1'}],
['link', 'image'],
['clean']
],
}
this.formats = [
'header',
'bold', 'italic', 'underline', 'strike', 'blockquote',
'list', 'bullet', 'indent',
'link', 'image'
]
}
handleEditorChange(value) {
this.setDocumentDetails('html_content', value)
}
render() {
let selectedDocument = this.state.documentDetails;
return (
<div>
<div>
<div className="form-group">
<label>File Content</label>
<Editor placeholder={'Write something or insert a star ★'}/>,
</div>
</div>
</div>
);
}
}
export default DocumentForm
/*
* Custom "star" icon for the toolbar using an Octicon
* https://octicons.github.io
*/
const CustomButton = () => <span className="octicon octicon-star" />
/*
* Event handler to be attached using Quill toolbar module
* http://quilljs.com/docs/modules/toolbar/
*/
function insertStar () {
const cursorPosition = this.quill.getSelection().index
this.quill.insertText(cursorPosition, "★")
this.quill.setSelection(cursorPosition + 1)
}
/*
* Custom toolbar component including insertStar button and dropdowns
*/
const CustomToolbar = () => (
<div id="toolbar">
<select className="ql-header" defaultValue={""} onChange={e => e.persist()}>
<option value="1"></option>
<option value="2"></option>
<option selected></option>
</select>
<button className="ql-bold"></button>
<button className="ql-italic"></button>
<select className="ql-color">
<option value="red"></option>
<option value="green"></option>
<option value="blue"></option>
<option value="orange"></option>
<option value="violet"></option>
<option value="#d0d1d2"></option>
<option selected></option>
</select>
<button className="ql-insertStar">
<CustomButton />
</button>
</div>
)
/*
* Editor component with custom toolbar and content containers
*/
class Editor extends React.Component {
constructor (props) {
super(props)
this.state = { editorHtml: '' }
this.handleChange = this.handleChange.bind(this)
}
handleChange (html) {
this.setState({ editorHtml: html });
}
render() {
return (
<div className="text-editor">
<CustomToolbar />
<ReactQuill
onChange={this.handleChange}
placeholder={this.props.placeholder}
modules={Editor.modules}
/>
</div>
)
}
}
/*
* Quill modules to attach to editor
* See http://quilljs.com/docs/modules/ for complete options
*/
Editor.modules = {
toolbar: {
container: "#toolbar",
handlers: {
"insertStar": insertStar,
}
}
}
/*
* Quill editor formats
* See http://quilljs.com/docs/formats/
*/
Editor.formats = [
'header', 'font', 'size',
'bold', 'italic', 'underline', 'strike', 'blockquote',
'list', 'bullet', 'indent',
'link', 'image', 'color',
]
Unlike the tutorial there were 2 toolbars
The custom toolbar was displayed but the buttons were not having styles.
Also the button handler(insertStar) is not working.
A screenshot of the page is as follows.
Any idea on how to fix this
In Reactquill documentation https://www.npmjs.com/package/react-quill#import-the-stylesheet the stylesheet must be imported as:
require('react-quill/dist/quill.snow.css'); // CommonJS
import 'react-quill/dist/quill.snow.css'; // ES6
According to the quill.js guide, you need to import the CSS like following
#import "~quill/dist/quill.core.css"
Try importing, the stylesheet like:
import "react-quill/dist/quill.snow.css"