draftjs how to initiate an editor with content

ReactjsDraftjs

Reactjs Problem Overview


Stumbled on this cool text editor, draft.js by Facebook. I tried to follow the example in Github, but I want to create an editor with content instead of an empty editor.

var EditorState = Draft.EditorState;

var RichEditor = React.createClass({
   getInitialState(){
      return {editorState: EditorState.createWithContent("Hello")}
      //the example use this code to createEmpty editor
     // return ({editorState: EditorState.createEmpty()})
   }
});

When I run it, it throws an error with the following message "Uncaught TypeError: contentState.getBlockMap is not a function".

Reactjs Solutions


Solution 1 - Reactjs

The first argument to EditorState.createWithContent is a ContentState, not a string. You need to import ContentState

var EditorState = Draft.EditorState;
var ContentState = Draft.ContentState;

Use ContentState.createFromText And pass the result to EditorState.createWithContent.

return {
  editorState: EditorState.createWithContent(ContentState.createFromText('Hello'))
};

Solution 2 - Reactjs

I've created a set of packages for DraftJS to help with importing and exporting content (HTML/Markdown). I use these in my project react-rte. The one you're probably looking for is: draft-js-import-html on npm.

npm install draft-js-import-html

An example of how you might use it:

var stateFromHTML = require('draft-js-import-html').stateFromHTML;
var EditorState = Draft.EditorState;

var RichEditor = React.createClass({
  getInitialState() {
    let contentState = stateFromHTML('<p>Hello</p>');
    return {
      editorState: EditorState.createWithContent(contentState)
    };
  }
});

The modules I've published are:

Solution 3 - Reactjs

You can use convertFromHTML to import html with createWithContent

import { convertFromHTML, ContentState } from 'draft-js'

const html = '<div><p>hello</p></div>'
const blocksFromHTML = convertFromHTML(html)
const content = ContentState.createFromBlockArray(blocksFromHTML)
this.state = { 
  editorState: EditorState.createWithContent(content)
}

As shown in Draft's convertFromHtml example. Note that the 0.9.1 version cannot import images, whereas 0.10.0 can.

In 0.10.0 createFromBlockArray changes to:

const content = ContentState.createFromBlockArray(
  blocksFromHTML.contentBlocks,
  blocksFromHTML.entityMap
)

Solution 4 - Reactjs

There has been some API changes, for clarity these examples uses latest API which is v0.10.0.

There are many ways but basically you got three options depending on whether you want to use plain text, styled text or html markup for content resource.

What plain text is obvious but for styled text, you need to use either serialized javasript object or html markup.

Lets start with plain text example:

import {Editor, EditorState} from 'draft-js';

class MyEditor extends Component{

  constructor(props) {
    super(props);

    const plainText = 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.';
    const content = ContentState.createFromText(plainText);

    this.state = { editorState: EditorState.createWithContent(content)};

    this.onChange = (editorState) => {
      this.setState({editorState});
    }
  }
  render(){
    return(
      <Editor
        editorState={this.state.editorState}
        onChange={this.onChange}
      />
    )
  }
}

For importing styled content Draft.js provides convertFromRaw and convertFromHTML utility functions.

convertFromRaw function takes raw javascript object as parameter. Here, we are using a JSON stringified javascript object as content source:

class MyEditor extends Component{

  constructor(props) {
    super(props);

    const rawJsText = `{
      "entityMap": {},
      "blocks": [
        {
          "key": "e4brl",
          "text": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit.",
          "type": "unstyled",
          "depth": 0,
          "inlineStyleRanges": [
            {
              "offset": 0,
              "length": 11,
              "style": "BOLD"
            },
            {
              "offset": 28,
              "length": 29,
              "style": "BOLD"
            },
            {
              "offset": 12,
              "length": 15,
              "style": "ITALIC"
            },
            {
              "offset": 28,
              "length": 28,
              "style": "ITALIC"
            }
          ],
          "entityRanges": [],
          "data": {}
        },
        {
          "key": "3bflg",
          "text": "Aenean commodo ligula eget dolor.",
          "type": "unstyled",
          "depth": 0,
          "inlineStyleRanges": [],
          "entityRanges": [],
          "data": {}
        }
      ]
    }`;

    const content  = convertFromRaw(JSON.parse(rawJsText));
    this.state = { editorState: EditorState.createWithContent(content)};

    this.onChange = (editorState) => {
      this.setState({editorState});
    }
  }
  render(){
    return(
      <Editor
        editorState={this.state.editorState}
        onChange={this.onChange}
      />
    )
  }
}

Draft.js provides convertToRaw function so that you can convert your editor's state to raw javascript object for long term storage.

And lastly, here how you do it with html markup:

class MyEditor extends Component{

  constructor(props) {
    super(props);
    
    const html = `<p>Lorem ipsum <b>dolor</b> sit amet, <i>consectetuer adipiscing elit.</i></p>
      <p>Aenean commodo ligula eget dolor. <b><i>Aenean massa.</i></b></p>`;
    
      const blocksFromHTML = convertFromHTML(html);
      const content = ContentState.createFromBlockArray(
        blocksFromHTML.contentBlocks,
        blocksFromHTML.entityMap
      );
    
    this.state = { editorState: EditorState.createWithContent(content)};

    this.onChange = (editorState) => {
      this.setState({editorState});
    }
  }
  render(){
    return(
      <Editor
        editorState={this.state.editorState}
        onChange={this.onChange}
      />
    )
  }
}

Solution 5 - Reactjs

When you need to initiate an editor with plain text.

Use EditorState.createWithContent and ContentState.createFromText methods. Working example - https://jsfiddle.net/levsha/3m5780jc/

constructor(props) {
  super(props);

  const initialContent = 'Some text';
  const editorState = EditorState.createWithContent(ContentState.createFromText(initialContent));

  this.state = {
    editorState
  };
}

When you need to initiate an editor with content from the html markup string.

Use convertFromHTML and ContentState.createFromBlockArray. Working example - https://jsfiddle.net/levsha/8aj4hjwh/

constructor(props) {
  super(props);

  const sampleMarkup = `
    	<div>
      	<h2>Title</h2>
        <i>some text</i>
      </div>
    `;

  const blocksFromHTML = convertFromHTML(sampleMarkup);
  const state = ContentState.createFromBlockArray(
    blocksFromHTML.contentBlocks,
    blocksFromHTML.entityMap
  );

  this.state = {
    editorState: EditorState.createWithContent(state),
  };
}

When you have an array of string and you want to initiate an editor with some of the default draft.js block types.

You can create array of ContentBlockss with constructor new ContentBlock(...), and pass it to ContentState.createFromBlockArray method. Working example with unordered-list-item - https://jsfiddle.net/levsha/uy04se6r/

constructor(props) {
  super(props);
  const input = ['foo', 'bar', 'baz'];

  const contentBlocksArray = input.map(word => {
    return new ContentBlock({
      key: genKey(),
      type: 'unordered-list-item',
      characterList: new List(Repeat(CharacterMetadata.create(), word.length)),
      text: word
    });
  });

  this.state = {
    editorState: EditorState.createWithContent(ContentState.createFromBlockArray(contentBlocksArray))
  };
}

When you need to initiate an editor with content from ContentState raw JS structure.

If you previously saved you content state to raw JS structure with convertToRaw (read this answer for details). You can initiate an editor with convertFromRaw method. Working example - https://jsfiddle.net/levsha/tutc419a/

constructor(props) {
  super(props);

  this.state = {
    editorState: EditorState.createWithContent(convertFromRaw(JSON.parse(editorStateAsJSON)))
  };
}

Solution 6 - Reactjs

In simple, you can set raw HTML content to editor from the initial stage or using setState at any time as below.

editorState: EditorState.createWithContent(ContentState.createFromBlockArray(convertFromHTML('<b>Program</b>')))

Import the necessary components.

import { EditorState, ContentState, convertFromHTML } from 'draft-js'

Solution 7 - Reactjs

You can set the initial value to your edtitorState just by add the folwwling code if you want to set it with HTML format

this.state = {
  editorState: EditorState.createWithContent(
    ContentState.createFromBlockArray(
      convertFromHTML('<p>My initial content.</p>')
    )
  ),
}

Solution 8 - Reactjs

I found this to be a clean way of doing things with rich functionality. You can add more plugins in the future, export your content as .md etc without changing the structure of your component much.

import Draft from 'draft-js';

import DraftPasteProcessor from 'draft-js/lib/DraftPasteProcessor';
const { EditorState, ContentState } = Draft;
import Editor from 'draft-js-plugins-editor';

import createRichButtonsPlugin from 'draft-js-richbuttons-plugin';
const richButtonsPlugin = createRichButtonsPlugin();

class DescriptionForm extends React.Component {

state = {
  editorState: this._getPlaceholder(),
}

_getPlaceholder() {
  const placeholder = 'Write something here';
  const contentHTML = DraftPasteProcessor.processHTML(placeholder);
  const state = ContentState.createFromBlockArray(contentHTML);
  return Draft.EditorState.createWithContent(state);
}

_onChange(editorState) {
  this.setState({
    editorState,
  });
}

render () {
    let { editorState } = this.state;
    return (
      <div>
          <Editor
            editorState={editorState}
            onChange={this._onChange.bind(this)}
            spellCheck={false}
            plugins={[richButtonsPlugin, videoPlugin]}
          />
      </div>
    );
  }
}

Solution 9 - Reactjs

for Nextjs

import React, { Component } from 'react';
import { EditorState, convertToRaw, ContentState, convertFromHTML } from 'draft- 
js';
import draftToHtml from 'draftjs-to-html';
import dynamic from 'next/dynamic';
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";

const Editor = dynamic(
() => import('react-draft-wysiwyg').then(mod => mod.Editor),
{ ssr: false }
)

let htmlToDraft = null;
if (typeof window === 'object') {
htmlToDraft = require('html-to-draftjs').default;
}

export default class EditorConvertToHTML extends Component {
constructor(props) {
    super(props);
    this.state = {
        editorState: EditorState.createEmpty(),
        contentState: ""
     }
    }

 componentDidMount() {

    const html = '<p>Hey this <strong>editor</strong> rocks d😀</p>';
    const contentBlock = htmlToDraft(html);
    console.log(contentBlock)
     if (contentBlock) {
        const contentState = 
        ContentState.createFromBlockArray(contentBlock.contentBlocks);
        const editorState = EditorState.createWithContent(contentState);
        console.log(editorState)
        this.setState(
            {
                editorState: EditorState.createWithContent(
                    ContentState.createFromBlockArray(
                        convertFromHTML(html)
                    )
                )
            }
          )
         }
        } 

     onEditorStateChange = (editorState) => {
     this.setState({
        editorState,
      });
     };

    render() {
    const { editorState } = this.state;
    console.log(this.state.editorState.getCurrentContent())
    return (
        <div>
            <Editor
                editorState={editorState}
                wrapperClassName="demo-wrapper"
                editorClassName="demo-editor"
                onEditorStateChange={this.onEditorStateChange}
            />
            <textarea
                disabled
                value= 
      {draftToHtml(convertToRaw(editorState.getCurrentContent()))}
            />
         </div>
        );
      }
        }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionvdj4yView Question on Stackoverflow
Solution 1 - ReactjsBrigandView Answer on Stackoverflow
Solution 2 - ReactjsssturView Answer on Stackoverflow
Solution 3 - ReactjssvnmView Answer on Stackoverflow
Solution 4 - ReactjssnnsnnView Answer on Stackoverflow
Solution 5 - ReactjsMikhail ShabrikovView Answer on Stackoverflow
Solution 6 - ReactjsGooglianView Answer on Stackoverflow
Solution 7 - ReactjsRami SalimView Answer on Stackoverflow
Solution 8 - Reactjskaushik94View Answer on Stackoverflow
Solution 9 - ReactjsDeepak SinghView Answer on Stackoverflow