Is there a way to get a Node View from a Node object?

I created a custom Node View for one of my nodes and have it updating the view using Steps similar to how jstleger0 described here (except using node.attrs[key] instead of doc.attrs[key]) whenever the content of the node changes. Essentially I’ll pass in the node and node view to the Step then update one of the nodes attributes and rerender the node view.

I noticed that when I undo long enough to the point that the node is removed from dom then redo that node back into existence, a new Node gets constructed with a new Node View (I assume) because continuing to redo causes the node attrs to update but any commands I run on the Node View are not visible. Using console.log, I can see the Node View still exists in memory but it is not the same Node View.

I think the solution might be either to get the corresponding Node View from the Node and use that to update the view… am I on to something?..

More problems :weary:… If I add a new node of the same type after already adding and interacting with the old one, the new one’s attrs are created and set the same as the previous one. Looks like using the Step method I described before sets the attr default somehow :sob::sob::sob:

I don’t think there is a problem—node views are created on demand to display nodes, but there is no guarantee that a given node view remains tied to a given node. In fact, nodes shouldn’t be treated as having identity at all—they just describe a given piece of document structure, but their object identity isn’t relevant. A single node object might appear in the document multiple times, for example, and replacing a node with another instance of the same structure will not cause the DOM to change at all.

How do I set and replace a text node as a child into my custom node/node view?

let tr = this.view.state.tr;
tr.insertText("hello!", this.getPos()+1);
this.view.dispatch(tr);

That’s as far as I’ve gotten but it only appends text. I can’t make sense of how to select and replace it once it’s there…

If you track your node view’s content node (keeping it in a property and updating it when the node view’s update method is called), you can use that to determine the node size and do something like tr.replace(this.getPos() + 1, this.getPos() + 1 + this.node.content.size, schema.text("new content")). (Or you can query this.view.state.doc for the node at getPos() to find the current node.)

Thank you! That really got me further along to a solution! I tried using tr.replace() just like you mentioned but it didn’t seem to work. It complained something about the open sides not fitting. Does this mean the new text content must be the same length as the one I’m replacing?

Taking inspiration from your response, I decided to do this instead:

        let innerTextSelection = TextSelection.create(
            this.view.docView.node,
            this.getPos()+1,
            this.getPos()+1+this.node.content.size                
        );
        tr.setSelection(
            innerTextSelection
        ).replaceSelectionWith(
            schema.text('new text!'),
            false   
        );

Weirdly enough though, That didn’t work either :frowning: however, it did have the handy side effect of deleting the inner text that was there before. So for now I just tacked on a tr.insertText("new text!", this.getPos()+1); after that to place my new text in. It’s hacky, I know :sweat_smile::sweat_smile:

I can’t seem to figure out why replaceSelectionWith() isn’t working. schema.text() returns a valid text node and the fact that when I call it, the inner text content of my node is removed seems to imply that I made the right selection. thoughts?