Cannot read property 'isTextblock' of undefined when focusing empty paragraph

I wrote a method that should replace a node with this definition:

    task:  {
        group: "component",
        selectable: true,
        draggable: true,
        attrs: { .....} 
   }

By a paragraph and set the cursor to that paragraph so that the user can continue typing.

This is the method:

    replaceNodeWithEmptyParagraph(nodeId: string){
        console.log('replaceNodeWithEmptyParagraph');
        const nodeAndPos = findNodeWithId(this.doc(), nodeId);
        if(nodeAndPos){
            const emptyParagraph = canvasSchema.nodes.paragraph.createAndFill();
            const tr = this.state().tr.replaceWith(nodeAndPos.pos, nodeAndPos.pos + nodeAndPos.node.nodeSize, emptyParagraph);
            console.log('replace with empty paragraph TR')
            this.dispatchTransactionToCanvas(tr);

            const selection = new TextSelection(this.doc().resolve(nodeAndPos.pos));
            const selectionTR = this.state().tr.setSelection(selection);
            console.log(`selection TR`);
            this.dispatchTransactionToCanvas(selectionTR);

            const selectionBeforeFocus = this.state().selection;
            console.log('selection before focus: ', selectionBeforeFocus.toJSON());
            console.log('Node at selection: ', this.doc().nodeAt(selectionBeforeFocus.$anchor.pos));
            console.log('focus');
            this.proseMirrorView.focus();
        }
    }

And this is the error I get:

The node at selection before focus is the paragraph I added.

Any ideas ?

I’m not sure whether this is the problem, but you are creating an invalid text selection — a text selection’s head an anchor should always point into inline positions, and the one you are creating is pointing before the paragraph, rather than into it.

hmm, why is it before the paragraph if that is the position I inserted the paragraph when I replaced the task with it ? Also, when I do a doc.nodeAt passing that position the paragraph is returned. Do you mean the position is the paragraph but it should rather be one of the paragraph inline contents ?

I tried adding a TextNode with an empty space to the paragraph and setting the selection to it instead. It doesn’t throw an error but the weird thing is that right after this.proseMirrorView.focus() a transaction is created which replace the selection with two empty paragraphs.

	const emptyText = canvasSchema.text("\u00A0");
	const emptyParagraph = canvasSchema.nodes.paragraph.create({}, emptyText);
	const tr = this.state().tr.replaceWith(nodeAndPos.pos, nodeAndPos.pos + nodeAndPos.node.nodeSize, emptyParagraph);
	console.log('replace with empty paragraph TR')
	this.dispatchTransactionToCanvas(tr);

	const selection = new TextSelection(this.doc().resolve(nodeAndPos.pos + 1));
	const selectionTR = this.state().tr.setSelection(selection);
	console.log(`selection TR`);
	this.dispatchTransactionToCanvas(selectionTR);

	const selectionBeforeFocus = this.state().selection;
	console.log('selection before focus: ', selectionBeforeFocus.toJSON());
	console.log('Node at selection: ', this.doc().nodeAt(selectionBeforeFocus.$anchor.pos));
	console.log('focus');
	this.proseMirrorView.focus();

There is no contradiction there — if you insert a paragraph at a given position, it will be inserted after that position, so the position points before it.

You don’t need to insert a space to put the cursor inside the paragraph, just add 1 (for the paragraph’s opening token) to the start position to get a valid position inside the empty paragraph. You might want to refer to this part of the guide.

Is that true only for insert or for replaceWith as well ? Because I am replacing the task node with the paragraph and not inserting a paragraph after the task node.

I went back to the previous version of the code (no white space) and I added + 1 here:
new TextSelection(this.doc().resolve(nodeAndPos.pos + 1));

The error is gone but it still creates one extra paragraph after focus.

Logs:

This is how the DOM looks before: image .

And after (with one extra empty paragraph):
image

This is how my document looks like right before focus:
image

Position 48 is the first task, that should remain there.
Position 49 is the empty paragraph that replaced the other task which was previously there.
Position 50 This is where the selection is pointing to.
Position 51 is the other paragraph that should remain there.

For some reason if I add a timeout it doesn’t create a new paragraph, it just works as expected.

    replaceNodeWithEmptyParagraph(nodeId: string){
        console.log('replaceNodeWithEmptyParagraph');
        const nodeAndPos = findNodeWithId(this.doc(), nodeId);
        if(nodeAndPos){
            const emptyParagraph = canvasSchema.nodes.paragraph.createAndFill();
            const tr = this.state().tr.replaceWith(nodeAndPos.pos, nodeAndPos.pos + nodeAndPos.node.nodeSize, emptyParagraph);
            console.log('replace with empty paragraph TR')
            this.dispatchTransactionToCanvas(tr);
            /** 
             * Without this timeout in addition to focusing on the new paragraph 
             * it also creates a new one on top of it.
             * */
            setTimeout(() => this.setFocusOnPosition(nodeAndPos.pos), 1);
            // this.setFocusOnPosition(nodeAndPos.pos);
        }
    }

    setFocusOnPosition(pos: number){
        const selection = new TextSelection(this.doc().resolve(pos + 1));
        const selectionTR = this.state().tr.setSelection(selection);
        console.log(`selection TR`);
        this.dispatchTransactionToCanvas(selectionTR);

        const selectionBeforeFocus = this.state().selection;
        console.log('selection before focus: ', selectionBeforeFocus.toJSON());
        console.log('Node at selection: ', this.doc().nodeAt(selectionBeforeFocus.$anchor.pos));
        console.log('focus');
        this.proseMirrorView.focus();
    }