I'm confused by position and transaction

So I have the following document structure

<gallery>
<image src='xxx.png'></image>
...
</gallery>

gallery is an atom node, containing a list of images. Each image has an attribute src.

now given an index to one of the images, I want to change its src

let tr = new Transform(galleryNode.child(0));       
tr.setNodeAttribute( 0, 'src',  'new_src_location');
this.outerView.dispatch(tr);

This won’t work, and I got an error saying that the node at position 0 doesn’t have the attribute “src”.

if I operate on the gallery node instead, I will see no error

let tr = new Transform(galleryNode);       
tr.setNodeAttribute( 0, 'src',  'new_src_location');
this.outerView.dispatch(tr);

This would work (no errors), but after the operation, if I inspect the document, the attribute hasn’t been changed.

The document doesn’t explain what it means by Pos in the function setNodeAttribute

Don’t create transforms with some specific node as constructor argument. Start a state transaction (view.state.tr) and update the content of the entire document in that.

I changed my code to the following:

            let tr = this.outerView.state.tr.setNodeAttribute(this.getPos()+1, 'src', 'new_file_src');
            this.outerView.dispatch(tr);

This works, but I don’t know how to reliably get the position of a node’s position relative to its parent.

Here I’m using getPos() +1 to change the first child, but if I need to change the 5th child, How can I know its position? There doesn’t seem to be a function to return a node’s position. Am I missing anything? Thank you.

Count node sizes of the sibling nodes in front of the target, or use ResolvedPos.posAtIndex.

Thank you. it works now. just log my solution in case it can help others:

I get the parent’s resolvedPos first: then used posAtIndex:

let rpos=parentNode.resolve(0);

let tr = this.outerView.state.tr.setNodeAttribute( this.getPos() + 1 + rpos.posAtIndex(child_index, 0), 'attr',  'new_attr');

Hi, I seem to have encountered a similar problem. I have a custom-question node and added a question-number ProseMirrorPlugin

export const InsertQuestion = Node.create({
  name: 'custom-question',

  group: 'block',

  atom: true,

  draggable: true,

  addAttributes() {
    return {
      questionNumber: {
        default: null,
      },
    }
  },
}) 
addProseMirrorPlugins() {
    return [
      new Plugin({
        key: new PluginKey('question-number'),
        appendTransaction: (transactions, _prevState, nextState) => {
          const tr = nextState.tr
          const { heading, 'custom-question': customQuestion } = nextState.schema.nodes
          let index = 1
          let modified = false

          if (transactions.some(transaction => transaction.docChanged)) {
            nextState.doc.descendants((node, pos) => {
              if (node.type === heading) {
                index = 1
              }
              if (node.type === customQuestion) {
                tr.setNodeAttribute(pos, 'questionNumber', index)
                index++
                modified = true
              }
            })
          }
          tr.setMeta('addToHistory', false)
          return modified ? tr : null
        },
      }),
    ]
  },
})

What it does is that when I insert the custom-question extension it will automatically update the questionNumber value

  <span class="question-number absolute left--25px top-0">
    {{ getQuestionNumber }}.
  </span>

At this point it seems to be working fine

But in my node view, there is a section that is displayed and hidden by a variable. analysis-template

  addNodeView() {
    return VueNodeViewRenderer(Component)
  },
     <section
       v-show="showAnalysis"
       class="analysis-container"
     >
       <div
         class="p-1 hover:bg-gray-200"
         @click="showAnalysis = false"
       >
         <i class="i-ic:baseline-close text-xl"></i>
       </div>
       <div class="analysis-content">
         <div class="analysis-item flex">
           <label class="shrink-0">【答案】</label>
           <div v-html="formatAnswers(getAnalysisData?.answers)"></div>
         </div>

         <div class="analysis-item flex">
           <label class="shrink-0">【解析】</label>
           <div>
             <div v-html="formatParse(getAnalysisData?.parse)"></div>
           </div>
         </div>
       </div>
     </section>

When I delete the currently selected node and expand the above analysis-template for the next question node I want to delete, showAnalysis = true, the node is deleted normally, and question-number ProseMirrorPlugin updates questionNumber value . At this time, the next ‘custom-question’ becomes the current ‘custom-question’ , and the analysis-template that was expanded before is automatically closed, and the next question node is expanded.

After deleting number 4 in the figure, number 5 will become 4. Normally, it will still expand analysis-template by default, but it is closed. Number 6 is expanded by default. If you keep deleting, it will always be the next ‘custom-question’ to be deleted in the view. Expand analysis-template by default

ecdaca59-53d2-4051-ae55-10625c32d689

But when I delete new Plugin('question-number'), the whole deletion process is normal. I am sure it is a problem with this plugin, but I don’t know where the problem is. Can you help me?

When I delete the question-number plugin, it is deleted normally. 5db89dd2-6986-4542-a828-ad4c53dc2fa7