Custom DOM Serializer

Is there currently any way to use a custom DOMSerializer for all nodes in an EditorView? I’m envisioning an entry on EditorProps similar to clipboardSerializer. My use case is building an editor that contains angular-enabled content types - this basically involves using Angular’s $compile service to generate angular-linked DOM trees which I would then like to hand off to Prosemirror to insert into the document (currently my use case primarily deals with “widgets” which will be acting as leaf nodes, although it may extend to block nodes in the future).

My current solution with 0.14 is to define nodeViews which accomplish this for the nodes that I would like to “angularize”, but it would be cleaner if I could have a single DOMSerializer which automatically angularized DOM representations of node types.

You can use the nodeViews prop to do this.

Ya, as I mentioned, that’s the way I’m currently doing this with 0.14. However, as far as I can tell, that requires each node which would like a custom renderer to register by name in a big object. Not the end of the world, but with a lot of different node types, it seems tedious to maintain, and a duplication of effort since these nodes are all also registered in the schema.

I was wondering if there was a way (or if a way could be added!) to swap out the render behavior for ALL nodes with a single option - this seems like it would be a pretty easy config option to add?

Anyway, I’m definitely liking the added functionality of nodeViews even as it stands today, it’s a huge improvement for my use case over some of the DOM hacking I was doing before to accomplish this!

I’m not really sure what you mean there – surely you’ll have to define this custom rendering behavior per node, since I doubt you want all nodes to look the same. So then how is it different from specifying a nodeViews prop? You could even generate that programatically from your schema if there’s repetetive code involved.

Basically with Angular when the DOM for a new node gets created, it needs to be “angularized” by a call that looks like this:

let dom = someRenderingCallThatProducesADomNode();
// $compile is a built-in Angular service.
// scope is a shared state dictionary/namespace for the editor that the angular
// compiled code gets "linked" against to resolve variables
dom = $compile(dom)(scope)[0]; 

The important thing is that $compile happens for every DOM node that wants to be angularized, and internally takes care of all the individual rendering details. It may or may not change the resulting DOM node, depending on what the view looks like. So in this case, a custom DOMSerializer could very elegantly take care of just adding an extra step in the “normal” node definition pipeline - just extend the prosemirror default rendering with the extra step of calling $compile() on each rendered DOM node before it gets inserted by PM into the DOM.

What does ‘angularizing’ mean in this context? Do you really need your text nodes or your paragraphs to be hooked into Angular? ProseMirror will also create DOM nodes that don’t directly originate from rendering a document node, will things break if you don’t call $compile on those?

Not at all, but for my application there, are a fair number of widgets that do want to be linked up. The programatic registration solution you’re describing is what I have implemented right now, but I saw the clipboard option and the DomSerializer class and thought it could be simpler to just be able to swap that functionality out wholesale.

As far as what angularizing means - it basically depends on what custom “types” (angular calls these Directives) are defined on the dom nodes. For nodes that don’t lean on angular-provided functionality (e.g. they don’t have any attributes or classes that the current angular configuration cares about), calling $compile on them will be a no-op. So prosemirror created DOM nodes and even paragraphs and text nodes shouldn’t be affected one way or another by piping them through $compile.

Just write a nodeViews object that wraps the nodes that need to be compiled. You can use something like the function below as the nodeview constructor for such nodes (assuming they are leaf nodes).

function compiledView(node) {
  let {dom} = DOMSerializer.renderSpec(document, node.type.spec.toDOM(node))
  return {dom: $compile(dom)}
}