draftjs how to initiate an editor with content
Asked Answered
R

10

72

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".

Rockafellow answered 9/3, 2016 at 6:2 Comment(0)
T
97

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'))
};
Thornhill answered 9/3, 2016 at 7:34 Comment(0)
H
50

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:

Handiness answered 15/3, 2016 at 9:31 Comment(3)
stateFromHTML() is super awesome since it handles html tags like <p> and <strong> BUT, if I pass it a string with <sub> or <sup> tags, it drops them. Any workarounds for that?Guilbert
Sorry to say, I don't have a great workaround for that, unless the parser is rewritten to handle more cases. To be honest the whole set of tooling I created (listed above) is terribly outdated and not really maintained anymore.Handiness
Great to have. Initializing the editor with createWithContent is not mentioned in the docs/repo pageBelligerent
W
22

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}
      />
    )
  }
}
Wu answered 2/3, 2017 at 9:44 Comment(1)
a little aside - I'm confused how data {} object can be used while importing with convertFromRaw() - are there any examples of use cases?Liatrice
Y
17

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
)
Yugoslavia answered 5/12, 2016 at 3:12 Comment(0)
X
7

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)))
  };
}
Xeric answered 31/10, 2017 at 8:23 Comment(1)
I was onto this problem of setting current value of Editor that has been passed as HTML string for about 2 days... Thanks man!! Your solution solved it!! Great Work!Trothplight
C
4

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'
Catlin answered 5/9, 2020 at 9:52 Comment(0)
P
1

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>
    );
  }
}
Pennypennyaliner answered 14/10, 2016 at 7:35 Comment(1)
Why the downvotes? My answer is correct. I asked to initiate with contentState rather than string as asked in the question.Pennypennyaliner
D
1

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>')
    )
  ),
}
Daciadacie answered 26/10, 2018 at 11:59 Comment(2)
where did you get convertFromHTML from?Blowhole
from draft.js itself draftjs.org/docs/api-reference-data-conversionDaciadacie
N
0

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>
        );
      }
        }
Naxos answered 31/1, 2022 at 15:14 Comment(0)
F
0

you can use insertText()

insertContent(){
    const contentState=editorState.getCurrentState();
    const selection=editorState.getSelection();
    const newContentState=Modifer.insertText(contentState,selection,'hello');
    const nextEditorState=EditorState.push(editorState,newContentState,'insert-character')
    setEditorState(nextEditorState)
}
Foppery answered 22/10, 2022 at 13:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.