How to display the saved content of CKEditor5 in React Js - javascript

I am using CKEditor5 React Component Framework.I have successfully integrated the CKEditor in my project.And Being Able to use it.
But the problem is that I have to save the Content of the editor to the database and then display it to website.. All I get in the content
<blockquote><p>Hello from CKEditor 5!</p></blockquote>
And While Displaying it does not applies the css of CkEditor to show ..
Setup for CKEDITOR IS
import React, { Component } from 'react';
import CKEditor from '#ckeditor/ckeditor5-react';
import Classiceditor from '#ckeditor/ckeditor5-build-classic';
export class ClassicEditor extends Component {
constructor(){
super();
this.state = {
content : ""
}
}
onCashange = data => {
console.log( "Called" );
this.setState({
content : data.getData()
})
}
render() {
Classiceditor.builtinPlugins.map( plugin => console.log(plugin.pluginName) );
console.log("State", this.state.content);
return (
<>
<div className='App'>
<h2> Using CKEditor</h2>
<CKEditor
editor = { Classiceditor }
data = "<p>Hello from CKEditor 5!</p>"
onInit = { editor => {
//console.log( 'Editor is ready to use!', editor )
} }
onChange = { ( event, editor ) => {
this.onCashange( editor );
// const data = editor.getData();
// this.onChange( data );
// //console.log( { event, editor, data } );
}}
onBlur = { editor =>
console.log("Blur", editor) }
onFocus = { editor => {
//console.log( "Focus", editor )
} }
/>
</div>
<div className='editor'>
{ this.state.content }
</div>
</>
)
}
}
export default ClassicEditor

There are 2 options:
Download or using script to get Full CSS and include it in your project.
Link here
Note: You have to add a parent container with a class name ck-content
Create a custom CKEditor 5 build from the source code with all the CSS (both UI and the content) extracted to a separate file
Advance Guide
You can see more here from the documentation:
Content Styles

React does not allow you to render html code directly. Instead you have to use dangerouslySetInnerHTML attribute to do so. Do the following to solve your problem,
<div dangerouslySetInnerHTML={this.createMarkup()} className='editor'></div>
and have a method on the class as
createMarkup = () => {
return { __html: this.state.content };
}
This will make sure that you are not rendering the raw HTML to the page.
You can read more about this here

you can just install npm package like npm i react-render-html
enter link description here
and use it like :
<div className='...'>
{renderHTML(someHTML)}
</div>

Here is an answer to above question:
<div style={{wordWrap:'break-word',display:'inline-block'}}>
<div className="editor" dangerouslySetInnerHTML={{_html:this.state.content}}/>
</div>

Related

Custom CKEditor TypeError: this.props.editor.create is not a function

I'm using the following JSX code with a custom CKEditor that I built using the online-builder When I add this editor to my node modules and try to use it like the following:
import React, { Component } from 'react';
//import Editor from 'ckeditor5-custom-build/build/ckeditor';
import ClassicEditor from '#ckeditor/ckeditor5-build-custom';
import { CKEditor } from '#ckeditor/ckeditor5-react'
const editorConfiguration = {
toolbar: [ 'heading', '|', 'bold', 'italic', 'underline', 'strikethrough', '|' ]
};
class App extends Component {
render() {
return (
<div className="App">
<h2>Using CKEditor 5 from online builder in React</h2>
<CKEditor
editor={ ClassicEditor }
config={ editorConfiguration }
data="<p>Hello from CKEditor 5!</p>"
onReady={ editor => {
// You can store the "editor" and use when it is needed.
console.log( 'Editor is ready to use!', editor );
} }
onChange={ ( event, editor ) => {
const data = editor.getData();
console.log( { event, editor, data } );
} }
onBlur={ ( event, editor ) => {
console.log( 'Blur.', editor );
} }
onFocus={ ( event, editor ) => {
console.log( 'Focus.', editor );
} }
/>
</div>
);
}
}
export default App;
I get an error stating that TypeError: this.props.editor.create is not a function. Is there some step that I am apparently missing? The help is much appreciated.
After hours of research online and trying to figure out where the custom build went wrong it turned out to be the most simple solution ever! In my custom build's src folder where the ckeditor.js file is located (containing the custom build before being built) I noticed at the bottom of the file it looked something like this export default { Editor, Watchdog }; Seeing this I immediately knew the solution was to import the custom editor as a named import. If you import it as a default import then you will not have any of the features of the editor but rather the entire export as shown above. Simple solution change the line that says import ClassicEditor from '#ckeditor/ckeditor5-build-custom'; to import {Editor as ClassicEditor} from '#ckeditor/ckeditor5-build-custom'; and now it will import the way it was meant to be imported.The interesting part about this, is that if you do not add Watchdog to your custom build (using the online-builder) then it should be imported as a default import as it will export as follows export default Editor;

CKEDITOR Can't see content area with DecoupledEditor

I'm using CKEDITOR5 with React and I have succeed to implement CKEDITOR in my project but when I go on my view I can only see the toolbar.
The content area is invisible until I click on it and the blue border appear.
If I remove my focus on it there is no border anymore.
Here is my code
import React, { Component } from 'react'
import CKEditor from '#ckeditor/ckeditor5-react';
import DecoupledEditor from '#ckeditor/ckeditor5-build-decoupled-document';
export default class BlogNew extends Component {
render() {
return (
<div>
<CKEditor
editor={ DecoupledEditor }
onInit={ editor => {
editor.ui.getEditableElement().parentElement.insertBefore(
editor.ui.view.toolbar.element,
editor.ui.getEditableElement()
);
} }
onChange={(event, editor) => {
const data = editor.getData();
this.setState({ text: data })
}}
/>
</div>
)}
DecoupledEditor is dedicated to integrations which require a customized UI.
Make sure to add the style to your component:
:root {
.ck.ck-editor__editable_inline {
border: 1px solid var(--ck-color-base-border);
}
}
see # ckeditor5-ui

CKEditor 5 React custom image upload

I have spent all day trying to get CKEditor with React to work. Everything seems to be okay except the images. I
I have a way to upload the images to my server already (azure). ALL I NEED is to know how to connect it to the CKEditor with React! I keep getting the error "Upload adapter is not defined."
<CKEditor
editor={ ClassicEditor }
data={this.state.body ? this.state.body : "<p>Body text...</p>"}
onInit={ editor => {
// You can store the "editor" and use when it is needed.
console.log( 'Editor is ready to use!', editor );
} }
onChange={ ( event, editor ) => {
const data = editor.getData();
console.log( { event, editor, data,}, "DATA" );
} }
// config={upload=this.uploadImage()}
/>
I'm guessing it has something to do with the config file? I already have the function that uploads the file and returns the URL, I just don't know where to add that into the CKEditor in React.
That error means there is no upload adapter connected.
First, you'll need to implement your own Custom Upload Adapter which handles uploading the images to the server. There’s a sample in this Stack Overflow response
And then, you link the editor to your upload adapter in your onInit method. Like so:
<CKEditor
editor={ClassicEditor}
data={this.state.body ? this.state.body : "<p>Body text…</p>"}
onInit={editor => {
// Connect the upload adapter using code below
editor.plugins.get("FileRepository").createUploadAdapter = function(loader) {
return new UploadAdapter(loader);
};
console.log("Editor is ready to use!", editor);
}}
onChange={(event, editor) => {
const data = editor.getData();
console.log({ event, editor, data }, "DATA");
}}
/>
UploaderAdapter in the code is the name of your UploadAdapter class implementation.

adding a new button to the toolbar of reactjs CK editor 5

The code is as follows.
import CKEditor from '#ckeditor/ckeditor5-react';
import ClassicEditor from '#ckeditor/ckeditor5-build-classic';
render() {
let selectedDocument = this.state.documentDetails;
return (
<div>
<div>
{
selectedDocument.html_content &&
<div className="form-group">
<CKEditor
editor={ ClassicEditor }
data={selectedDocument.html_content }
onInit={ editor => {
} }
onChange={ ( event, editor ) => {
const data = editor.getData();
let obj = {
target: {
name: 'html_content',
value: data
}
}
this.handleChange(obj);
} }
/>
</div>
}
</div>
</div>
);
}
Using the above code, the editor is displayed with basic toolbar buttons.
Now I need to add a new button to the toolbar which when clicked a javascript function needs to be called.
I have seen documentation on creating custom plugins, but I am not sure on how to implement this in the react way.
Any ideas on how to implement this? A sample piece of code would e really helpful.
Find config.js in CKEditor folder and update config.extraPlugins with your plugin like config.extraPlugins = 'html5video,widget,widgetselection,clipboard,lineutils,videoembed';
It will display new toolbar or button on CKEditor.

How to render HTML string as real HTML?

Here's what I tried and how it goes wrong.
This works:
<div dangerouslySetInnerHTML={{ __html: "<h1>Hi there!</h1>" }} />
This doesn't:
<div dangerouslySetInnerHTML={{ __html: this.props.match.description }} />
The description property is just a normal string of HTML content. However it's rendered as a string, not as HTML for some reason.
Any suggestions?
Is this.props.match.description a string or an object? If it's a string, it should be converted to HTML just fine. Example:
class App extends React.Component {
constructor() {
super();
this.state = {
description: '<h1 style="color:red;">something</h1>'
}
}
render() {
return (
<div dangerouslySetInnerHTML={{ __html: this.state.description }} />
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
Result: http://codepen.io/ilanus/pen/QKgoLA?editors=1011
However if description is <h1 style="color:red;">something</h1> without the quotes '', you're going to get:
​Object {
$$typeof: [object Symbol] {},
_owner: null,
key: null,
props: Object {
children: "something",
style: "color:red;"
},
ref: null,
type: "h1"
}
If It's a string and you don't see any HTML markup the only problem I see is wrong markup..
UPDATE
If you are dealing with HTML Entities, You need to decode them before sending them to dangerouslySetInnerHTML that's why it's called "dangerously" :)
Working example:
class App extends React.Component {
constructor() {
super();
this.state = {
description: '<p><strong>Our Opportunity:</strong></p>'
}
}
htmlDecode(input){
var e = document.createElement('div');
e.innerHTML = input;
return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
}
render() {
return (
<div dangerouslySetInnerHTML={{ __html: this.htmlDecode(this.state.description) }} />
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
I use 'react-html-parser'
yarn add react-html-parser
import ReactHtmlParser from 'react-html-parser';
<div> { ReactHtmlParser (html_string) } </div>
Source on npmjs.com
Lifting up #okram's comment for more visibility:
from its github description: Converts HTML strings directly into React
components avoiding the need to use dangerouslySetInnerHTML from
npmjs.com A utility for converting HTML strings into React components.
Avoids the use of dangerouslySetInnerHTML and converts standard HTML
elements, attributes and inline styles into their React equivalents.
Check if the text you're trying to append to the node is not escaped like this:
var prop = {
match: {
description: '<h1>Hi there!</h1>'
}
};
Instead of this:
var prop = {
match: {
description: '<h1>Hi there!</h1>'
}
};
if is escaped you should convert it from your server-side.
The node is text because is escaped
The node is a dom node because isn't escaped
If you have HTML in a string I would recommend using a package called html-react-parser.
Installation
NPM:
npm install html-react-parser
yarn:
yarn add html-react-parser
Usage
import parse from 'html-react-parser'
const yourHtmlString = '<h1>Hello</h1>'
code:
<div>
{parse(yourHtmlString)}
</div>
If you have control over where the string containing html is coming from (ie. somewhere in your app), you can benefit from the new <Fragment> API, doing something like:
import React, {Fragment} from 'react'
const stringsSomeWithHtml = {
testOne: (
<Fragment>
Some text <strong>wrapped with strong</strong>
</Fragment>
),
testTwo: `This is just a plain string, but it'll print fine too`,
}
...
render() {
return <div>{stringsSomeWithHtml[prop.key]}</div>
}
I use innerHTML together a ref to span:
import React, { useRef, useEffect, useState } from 'react';
export default function Sample() {
const spanRef = useRef<HTMLSpanElement>(null);
const [someHTML,] = useState("some <b>bold</b>");
useEffect(() => {
if (spanRef.current) {
spanRef.current.innerHTML = someHTML;
}
}, [spanRef.current, someHTML]);
return <div>
my custom text follows<br />
<span ref={spanRef} />
</div>
}
UPDATE:
I removed someHTML state and added comments to make the example more coincise around the concept.
/**
* example how to retrieve a reference to an html object
*/
import React, { useRef, useEffect } from 'react';
/**
* this component can be used into another for example <Sample/>
*/
export default function Sample() {
/**
* 1) spanRef is now a React.RefObject<HTMLSpanElement>
* initially created with null value
*/
const spanRef = useRef<HTMLSpanElement>(null);
/**
* 2) later, when spanRef changes because html span element with ref attribute,
* follow useEffect hook will triggered because of dependent [spanRef].
* in an if ( spanRef.current ) that states if spanRef assigned to valid html obj
* we do what we need : in this case through current.innerHTML
*/
useEffect(() => {
if (spanRef.current) {
spanRef.current.innerHTML = "some <b>bold</b>";
}
}, [spanRef]);
return <div>
my custom text follows<br />
{/* ref={spanRef] will update the React.RefObject `spanRef` when html obj ready */}
<span ref={spanRef} />
</div>
}
You just use dangerouslySetInnerHTML method of React
<div dangerouslySetInnerHTML={{ __html: htmlString }} />
Or you can implement more with this easy way: Render the HTML raw in React app
In my case, I used react-render-html
First install the package by npm i --save react-render-html
then,
import renderHTML from 'react-render-html';
renderHTML("<a class='github' href='https://github.com'><b>GitHub</b></a>")
I could not get npm build to work with react-html-parser. However, in my case, I was able to successfully make use of https://reactjs.org/docs/fragments.html. I had a requirement to show few html unicode characters , but they should not be directly embedded in the JSX. Within the JSX, it had to be picked from the Component's state. Component code snippet is given below :
constructor()
{
this.state = {
rankMap : {"5" : <Fragment>★ ★ ★ ★ ★</Fragment> ,
"4" : <Fragment>★ ★ ★ ★ ☆</Fragment>,
"3" : <Fragment>★ ★ ★ ☆ ☆</Fragment> ,
"2" : <Fragment>★ ★ ☆ ☆ ☆</Fragment>,
"1" : <Fragment>★ ☆ ☆ ☆ ☆</Fragment>}
};
}
render()
{
return (<div class="card-footer">
<small class="text-muted">{ this.state.rankMap["5"] }</small>
</div>);
}
i use https://www.npmjs.com/package/html-to-react
const HtmlToReactParser = require('html-to-react').Parser;
let htmlInput = html.template;
let htmlToReactParser = new HtmlToReactParser();
let reactElement = htmlToReactParser.parse(htmlInput);
return(<div>{reactElement}</div>)
You can also use parseReactHTMLComponent from Jumper Package. Just look at it, it's easy and you don't need to use JSX syntax.
https://codesandbox.io/s/jumper-module-react-simple-parser-3b8c9?file=/src/App.js .
More on Jumper:
https://github.com/Grano22/jumper/blob/master/components.js
NPM Package:
https://www.npmjs.com/package/jumper_react
// For typescript
import parse, { HTMLReactParserOptions } from "html-react-parser";
import { Element } from "domhandler/lib/node";
export function contentHandler(postContent: string) {
const options: HTMLReactParserOptions = {
replace: (domNode: Element) => {
if (domNode.attribs) {
if (domNode.attribs.id === 'shortcode') {
return <div className="leadform">Shortcode</div>;
}
}
},
};
return parse(postContent, options);
}
// Usage: contentHandler("<span>Hello World!</span>")
If you have control to the {this.props.match.description} and if you are using JSX. I would recommend not to use "dangerouslySetInnerHTML".
// In JSX, you can define a html object rather than a string to contain raw HTML
let description = <h1>Hi there!</h1>;
// Here is how you print
return (
{description}
);

Categories