Draft-js: How to add element by coding in Draft.js?

Created on 27 Jun 2016  路  10Comments  路  Source: facebook/draft-js

Hello,

I am new on Draft.js and able to create a decent enough text editor for my consumption, But I need to add a UL list with LI items at the bottom of my textarea by coding.

I have tried using Import HTML and then adding my HTML into it and then Exporting it back to the editor state, But Undo and Redo are not working after this.

Thank You..

Most helpful comment

I have refined the method so now it inserts blocks from HTML conversion at the current selection. For now, it inserts the new blocks AFTER the block that is the anchor of selection.

insertBlocksFromHtml(htmlString){
        const editorState = this.state.editorState;
        const newBlockMap = convertFromHTML(bulletsHTML);
        const contentState = editorState.getCurrentContent();
        const selectionState = editorState.getSelection();
        const key = selectionState.getAnchorKey();
        const blocksAfter =  contentState.getBlockMap().skipUntil(function (_, k) {
            return k === key;
        }).skip(1).toArray();
        const blocksBefore =  contentState.getBlockMap().takeUntil(function (_, k) {
            return k === key;
        }).toArray();

        newBlockMap.contentBlocks =
            blocksBefore
                .concat([contentState.getBlockForKey(key)])
                .concat(newBlockMap.contentBlocks)
                .concat(blocksAfter);

        const newContentState =
            ContentState.createFromBlockArray(newBlockMap,newBlockMap.entityMap);
        const newEditorState = EditorState.createWithContent(newContentState);
        this.setState({editorState: newEditorState});
    }

All 10 comments

Hey Rahul, I got your email asking for help here. The description you have above seems vague, but I am guessing you contacted me because of draft-js-gutter. If you are just trying to toggle that on and off, I would do an inline style like so:

import React, { Component } from 'react';
import { EditorGutter } from './Draft-js-gutter';

class ListedBox extends React.Component {
  render() {
    var displayList = 'none';
    if (/* Logic if you want the list*/) {
      displayList = 'initial'
    }
    return (
      <EditorGutter
        styleList={{
          display: displayList
        }}
      />
    );
  }
}
export default ListedBox;

I know I have not updated the dependency in a long time, but go ahead and make an issue on that repo instead if this is what you are dealing with. That way we keep this issue list more relatable. Cheers!

Hello,

Thank you for responding to the mail. My issue that I am facing is to add a list item in the end of my editor state.

For eg: in my Editor
I have written
"This is Github.
I am asking Question."

Now on click of a button I want to append an ordered list item like
"This is Github.
I am asking Question.
1. Welcome."

I have tried using this method

let currentHTML = stateToHTML(this.state.editorState.getCurrentContent());
currentHTML =   currentHTML + "<ol><li>Welcome</li></ol";
let contentState = stateFromHTML(currentHTML);
this.setState({editorState : EditorState.createWithContent(contentState)});

But using this method creates a new editor state with cleared undo redo stack.

So my question how to append a new block in my editor content state.

Okay, I got what I want. i have done it using two methods
1.)

        let rawObject = convertToRaw(this.state.editorState.getCurrentContent());
        var enterObject = {};
        const processedHTML = DraftPasteProcessor.processHTML("<ol><li>Welcome</li></ol>");
        enterObject.depth = processedHTML[0].depth;
        enterObject.key = processedHTML[0].key;
        enterObject.text = processedHTML[0].text;
        enterObject.type = processedHTML[0].type;
        enterObject.depth = processedHTML[0].depth;
        enterObject.entityRanges = [];
        enterObject.inlineStyleRanges = [];
        rawObject .blocks.push(enterObject);
        let newContentState = convertFromRaw(x);
        var esTemp =  EditorState.push(this.state.editorState, newContentState, 'insert-fragment');
        this.setState({editorState: esTemp });

2.)

      EditorState.moveFocusToEnd(this.state.editorState);
      let currentHTML = stateToHTML(this.state.editorState.getCurrentContent());
      currentHTML =   currentHTML + "<ol><li>Welcome</li></ol>";
      let contentState = stateFromHTML(currentHTML);
      this.setState({editorState: EditorState.push(this.state.editorState, contentState, 'insert-fragment')});

Does anyone knows any other way to append a new content block inside the draftjs texteditor ?
I have few buttons and on the click of them I want to append unordered/ordered list item inside the text editor.

On internet we have ConvertToHTML and ConvertFromHTML but they do not support on IE. And there is some real drought of codes which may help in appending new blocks inside draftJS.

@seejamescode @AlastairTaft @hellendag

@Rahulgarg30591 thanks you for you code, I was able to wrap it in a simple function to append HTML to the content state.

function insertHtmlBlock(currentEditorState, html, state) {
  const rawObject = convertToRaw(currentEditorState.getCurrentContent());
  const enterObject = {};
  const processedHTML = DraftPasteProcessor.processHTML(html);
  enterObject.depth = processedHTML[0].depth;
  enterObject.key = processedHTML[0].key;
  enterObject.text = processedHTML[0].text;
  enterObject.type = processedHTML[0].type;
  enterObject.depth = processedHTML[0].depth;
  enterObject.entityRanges = [];
  enterObject.inlineStyleRanges = [];
  rawObject.blocks.push(enterObject);
  const newContentState = convertFromRaw(rawObject);
   state.setState({editorState: EditorState.push( currentEditorState, newContentState, 'insert-fragment')});
}

very helpful

Really? There is no built-in way to append a block using html text? This is kind of bad...

I am only starting with this ... so take this with a grain of salt..this method seems to be appending blocks resulting from HTML conversion:

appendBlocksFromHtml(htmlString){
        const editorState = this.state.editorState;
        const newBlockMap = convertFromHTML(htmlString);
        const contentState = editorState.getCurrentContent();
        const blockMap = contentState.getBlocksAsArray();

        newBlockMap.contentBlocks = blockMap.concat(newBlockMap.contentBlocks);

        const newContentState =
            ContentState.createFromBlockArray(newBlockMap,newBlockMap.entityMap);
        const newEditorState = EditorState.createWithContent(newContentState);
        this.setState({editorState: newEditorState});
}

I have refined the method so now it inserts blocks from HTML conversion at the current selection. For now, it inserts the new blocks AFTER the block that is the anchor of selection.

insertBlocksFromHtml(htmlString){
        const editorState = this.state.editorState;
        const newBlockMap = convertFromHTML(bulletsHTML);
        const contentState = editorState.getCurrentContent();
        const selectionState = editorState.getSelection();
        const key = selectionState.getAnchorKey();
        const blocksAfter =  contentState.getBlockMap().skipUntil(function (_, k) {
            return k === key;
        }).skip(1).toArray();
        const blocksBefore =  contentState.getBlockMap().takeUntil(function (_, k) {
            return k === key;
        }).toArray();

        newBlockMap.contentBlocks =
            blocksBefore
                .concat([contentState.getBlockForKey(key)])
                .concat(newBlockMap.contentBlocks)
                .concat(blocksAfter);

        const newContentState =
            ContentState.createFromBlockArray(newBlockMap,newBlockMap.entityMap);
        const newEditorState = EditorState.createWithContent(newContentState);
        this.setState({editorState: newEditorState});
    }

@andpor , please what if the editor had a non-empty entityMap before the insertion ? should I also concatenate it with newBlock.entityMap before creating the newEditorState ?

I was adding lots of "decorated" atomic blocks and the some editable content in between, the first snippet @andpor put here worked great, but the order was off. So what solved it was wrapping the last line with EditorState.moveSelectionToEnd(
like this

```language:js
function appendBlocksFromHtml(editorState, htmlString) {
const newBlockMap = htmlToDraft(htmlString);
const contentState = editorState.getCurrentContent();
const blockMap = contentState.getBlocksAsArray();
newBlockMap.contentBlocks = blockMap.concat(newBlockMap.contentBlocks);

const newContentState = ContentState.createFromBlockArray(newBlockMap, contentState.getEntityMap());
return EditorState.moveSelectionToEnd(EditorState.createWithContent(newContentState));
}
```

Was this page helpful?
0 / 5 - 0 ratings