On paste: Default node attrs only in first instance

Given this schema:

    paragraph: {
      content: 'text*',
      attrs: {
        align: { default: 'left' }
      },
      toDOM (node) {
        return [
          'p',
          {
            class: 'editor__textBlock',
            align: node.attrs.align
          },
          0
        ]
      },
      parseDOM: [
        {
          tag: 'p',
          getAttrs (dom) {
            return {
              align: dom.getAttribute('align')
            }
          }
        },
        { tag: 'div' }
      ]
    }

If I paste unformatted text with paragraphs, only the first paragraph gets the default “align” attribute set in dom.

Is this expected?

A simple fix to this is btw:

    paragraph: {
     ...
      toDOM (node) {
        return [
          'p',
          {
            class: 'editor__textBlock',
            align: node.attrs.align || 'left'   // <------------
          },
          0
        ]
      },
     ...

I use:

prosemirror-model 1.17.0

Yes, this is expected—your getAttrs will return {align: null} when the element has no align attribute, and default values are only filled in for attributes that are not provided or have undefined as value.

Hey. Thx for the quick answer!

your getAttrs will return {align: null} when the element has no align attribute

makes sense

default values are only filled in for attributes that are not provided

The pasted text (paragraphs) has no ‘align’ attribute. So didn’t this count ‘as not provided’?

What also puzzles me: Why did the first paragraph get the attribute set (with default value) but the following paragraphs do not?

getAttribute on a non-existent attribute returns null.

You are pasting your content into an empty document that already starts with a default-attribute paragraph, I assume. Since paragraphs aren’t set as defining, that will be preserved to hold the first line of the content.

TLDR: I got it now. What I didn’t understand is that parseDOM is used also on paste actions (I didn’t thought of it as DOM but plain text). So my explicit use of ‘getAttribute’ causes all the trouble. Sorry for wasting your time. Thx for the help anyways!!


document that already starts with a default-attribute paragraph, I assume

Hmm. Yes. Sounds reasonable.

But I can step one up :slight_smile:

I have actually two default attributes defined (initially omitted to simplify).

And the second default attribute is set on all pasted paragraphs:

<p class="editor__textBlock" align="left" ghost="false">Lorem ipsum dolor sit amet,</p>
<p class="editor__textBlock" ghost="false">Lorem ipsum dolor sit amet</p>
<p class="editor__textBlock" ghost="false">Lorem ipsum dolor sit amet</p>
<p class="editor__textBlock" ghost="false">Lorem ipsum dolor sit amet,</p>

Here is a glitch.

Here the updated schema:

const mySchema = new Schema({
  nodes: {
    doc: { content: 'paragraph+' },
    paragraph: {
      content: 'text*',
      attrs: {
        align: { default: 'left' },
        ghost: { default: false }
      },
      toDOM (node) {
        return [
          'p',
          {
            class: 'editor__textBlock',
            align: node.attrs.align,
            ghost: node.attrs.ghost
          },
          0
        ]
      },
      parseDOM: [
        {
          tag: 'p',
          getAttrs (dom) {
            return {
              align: dom.getAttribute('align')
            }
          }
        },
        { tag: 'div' }
      ]
    },
    text: { inline: true }
  },
  marks: schema.spec.marks
})

Because you’re not setting that to null in parseDOM?

Because you’re not setting that to null in parseDOM ?

Yes. I didn’t assumed ‘parseDOM’ to be involved in paste actions. But that turned out to be a wrong assumption.