I have a hierarchical document tree that I want to concatenate into a single editor. In order to track changes to the original document, is there a way I can pass custom Id values into the editor state? so, for instance:
const docTree = [
{id: 0, body: "chapter one", children:[
{id: 1, body: "section one of chapter one"},
{id: 2, body: "section two of chapter one"}]},
{id: 3, body: "chapter 2..."}
]
Right now, I’m just reducing the body strings themselves and doing…
So that works fine. But when I make changes, I need to somehow know what original doc id I was typing in. So what would I have to pass into EditorState.create in order to retain those ids? Would I extend the nodes schema to do this?
In our implementation, we add the attribute id to the element definition itself when we create the schema. By storing the id in an attribute it gets retained even when copying, serializing or exporting to HTML.
We then use a plugin to make sure all IDs are unique (which is why we do not initially create them). The gist is:
In appendTransaction use newState.doc.descendants to loop through all nodes. If an attribute id exists, check whether it is unique in the document (e.g. by keeping a hashset of existing ids) and generate a new one if needed. You may want to register a collision somewhere so you can make sure it can be tracked back to the original document.
Yes, this is great! I think the only thing I’m missing is the right way to set the attributes when setting the editor state? I would also need a way to, for instance, bind a key command to create a new element with a brand new ID…
When you’re setting the editor state you provide a document, if that already has the attribute id in the nodes, then ProseMirror will have that as well.
So if I do something like
const dom = document.createElement('div');
dom.innerHTML = `<p id="one">my text</p>`;
const doc = DOMParser.fromSchema(mySchema).parse(dom);
const state = EditorState.create({ doc, .. });
The id will be in there (since the schema knows how to parse the DOM. Of course you could also create the document from serialized JSON.
The JSON that ProseMirror uses internally will look sth. like this
If you add a new element (e.g. by pressing Enter in the editor) your ID plugin (as mentioned above) will detect a new element without an ID and will add one.
Awesome—I’m inching closer but for some reason 1) the id isn’t being parsed even though I use that identical parseDOM function, and also now the editor UI won’t actually reflect any typing. Is there a gist of a complete example somewhere? There are just so many elements to this API I know I’m still missing something.
In the (browser) console output, you can see where parseDOM correctly extracts the supplied custom node and ID attribute. However, toDOM then shows that the ID is missing (undefined), and any subsequent look at the ProseMirror document shows that the IDs are missing.
It seems like there’s some step or element I’m not doing to make this work…?