Why is NodeView.update called with different node types?

My app had an odd bug. I’ve fixed it, but I’m confused about why things works this way. And given everything in ProseMirror is so carefully considered, I figure I might be missing something important.

I’m adding image support to my app, and have added a node type blockImage, and a node-view that handles drag-and-drop of image files. I had a weird bug where, on deleting an image, whatever block was next in the doc was erroneously rendered using the image node view, even if it wasn’t an image. It looked like the block had vanished.

I checked out the guide and this is indeed expected. e.g. see this example:

let view = new EditorView({
  state,
  nodeViews: {
    paragraph(node) { return new ParagraphView(node) }
  }
})

class ParagraphView {
  constructor(node) {
    this.dom = this.contentDOM = document.createElement("p")
    if (node.content.size == 0) this.dom.classList.add("empty")
  }

  update(node) {
    if (node.type.name != "paragraph") return false // <<<<<<<<< The fix!
    if (node.content.size > 0) this.dom.classList.remove("empty")
    else this.dom.classList.add("empty")
    return true
  }
}

Adding a similar line to my image node view fixed the problem.

My question is (I’ll write it in terms of this example code, not my app), given the node view is registered specifically for paragraphs, why would .update be called with some other node type? Surely the only sensible thing to do in that case would be to remove the node view from the dom?

Put another way, would one ever write a node view without that “if not paragraph return false”? Why?

It is possible for a node view to support several types of nodes, and be able to update from one to another. But yeah, if I were to design the interface now I’d make that require an explicit opt-in, since despite the docs clearly mentioning this, it keeps tripping people up.

1 Like

Ahh that makes sense. I can see this being useful if a schema had a bunch of closely related node types.

Maybe it’s possible to address this in a backwards-compatible way?

e.g. the editor knows if the node-view has been registered against multiple node types. If not, the equivalent of “if different type return false” could be the default behaviour.

I guess that could break existing code if someone has registered a node-view against only one type, but later can handle multiple. That would seem like an odd thing to do, but still possible.

Tom

The only way I can see such a check being practical is to compare the node view constructor functions by identity, which would fail on something like…

{
  node1(view) { return new MyNodeView(view, 1) },
  node2(view) { return new MyNodeView(view, 2) },
}
1 Like