Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem with Multiple Editors and v-for when deleting/splice an element in the array #3067

Closed
2 tasks done
nadar opened this issue Aug 9, 2022 · 17 comments
Closed
2 tasks done
Assignees
Labels
Type: Bug The issue or pullrequest is related to a bug
Milestone

Comments

@nadar
Copy link

nadar commented Aug 9, 2022

What’s the bug you are facing?

We know its always hard to understand others projects setup as maintainer, so i try to explain this quickly:

We use tiptap as a very versatile place to map data and mixed content, like a very smart input text field :-)

image

Now we have a very strange behavior of that "multiple tiptap editor" setup (they do not sharing anything to each other), when we remove an element in that array (this is what the iteration looks like and integration-map-field holds the tiptap editor instance):

<template v-for="(field,index) in item.config_json.fields">
    <integration-map-field
      :key="index"
      v-model="item.config_json.fieldMap[field.identifier]"
      :editable="true"
      :label="field.label"
      :type="field.type"
      :required="field.required"
      :fields-collection="item.pool.typeEntity.fieldsCollection"
      @remove="removeField(item, index, field.identifier)"
    />
  </template>

then it removes the element, but at the absolute wrong position, its no matter what, always removes the latest tiptap editor. For example we remove this one (array.splice(....)):

image

It removes the wrong editor, it always removes the last tiptap editor, but as you can see the array iteration has been correctly adjusted, the field title has been removed from the array:

image

This is so strange, i could not find anything regarding this. So i was looking for "uniqueids" or whatever. Maybe there is global instanced variable i need to consider?

  • i played around with this.editor.destroy() without any luck
  • i tested with vue's $forceUpdate()
  • i tested without tiptap (just added a textarea) and everything works expected.
  • my environment is nuxt 2 with vuetify

Which browser was this experienced in? Are any special extensions installed?

Chrome, Firefox. It happens on all browsers

How can we reproduce the bug on our side?

Take a look here: https://codesandbox.io/s/tiptap-vue-multiple-editor-issue-oxr1lo?file=/src/App.vue

Can you provide a CodeSandbox?

Update: Yes, its easy to reproduce, just add 3 or more items and remove an item in the middle: https://codesandbox.io/s/tiptap-vue-multiple-editor-issue-oxr1lo?file=/src/App.vue

What did you expect to happen?

It should removed the editor in the row, not the last one. it always removes the last one no matter what we do :-)

Anything to add? (optional)

This is what the component looks like ... i have removed unnecessary logic and markup for better readability:

The Component
<template>
  <div>
      <editor-content
        :editor="editor"
      />
      <v-btn
          icon
          @click="removeItem"
        >
          <v-icon small>icon icon-remove</v-icon>
      </v-btn>
  </div>
</template>

<script>
// https://stackoverflow.com/questions/48145727/insert-character-at-cursor-position-in-vue-js
import IntegrationMapFieldMapperInputSpan from './IntegrationMapFieldMapperInputSpan.vue'
import { Editor, EditorContent, Node, VueNodeViewRenderer, mergeAttributes} from '@tiptap/vue-2'
import Document from '@tiptap/extension-document'
import Text from '@tiptap/extension-text'

const Div = Node.create({
  name: 'div',
  group: 'block',
  content: 'inline*',
  parseHTML() {
    return [
      { tag: 'div'},
    ]
  },
  renderHTML({ HTMLAttributes }) {
    return ['div', mergeAttributes(HTMLAttributes, {style:"vertical-align: text-top; line-height:30px"}), 0]
  },
})

const Variable = Node.create({
  name: 'variable',
  group: 'inline',
  inline: true,
  atom: true,
  selectable: false,
   addAttributes() {
   // removed the attributes
  },
  parseHTML() {
    return [
      { tag: 'span' },
    ]
  },
  renderText({ node }) {
    return `${node.attrs.label}`
  },
  renderHTML({ node, HTMLAttributes }) {
    return ['span', mergeAttributes(HTMLAttributes), node.attrs.label]
  },
  addNodeView() {
    return VueNodeViewRenderer(IntegrationMapFieldMapperInputSpan)
  }
})

export default {
  components: { EditorContent },
  props: {
    // removed the props
  },
  data:() => ({
    editor: null
  }),
  computed: {
    parsedValue: function() {
     // we do more things here, fulfill the array
      return {
        "type": "doc",
        "content": contentArrays
      }
    }
  },
  mounted() {
    this.editor = new Editor({
      extensions: [
        Document,
        Div,
        Text,
        Variable
      ],
      autofocus: 'end',
      content: this.parsedValue,
      onUpdate: () => {
        const values = []
        const content = this.editor.getJSON().content
        // we do more things here
        this.$emit('input', values)
      },
    })
  },
  beforeDestroy() {
    this.editor.destroy()
  },
  methods: {
    removeItem() {
      this.$emit('removeItem')
    }
  }
}
</script>

Did you update your dependencies?

  • Yes, I’ve updated my dependencies to use the latest version of all packages.

Are you sponsoring us?

  • Yes, I’m a sponsor. 💖
@nadar nadar added the Type: Bug The issue or pullrequest is related to a bug label Aug 9, 2022
@nadar
Copy link
Author

nadar commented Aug 9, 2022

I made a codesandbox, its very easy to reproduce. https://codesandbox.io/s/tiptap-vue-multiple-editor-issue-oxr1lo?file=/src/App.vue

add 3 are more items, delete the one in the middle and magically the last editor is always deleted. 😭

@bdbch
Copy link
Contributor

bdbch commented Aug 22, 2022

Hey @nadar.

I'm going to take a look into this issue to find out what exactly is happening here. Thanks for reporting!

@bdbch bdbch self-assigned this Aug 22, 2022
@nadar
Copy link
Author

nadar commented Aug 22, 2022

@bdbch thank you for taking care of this. Let me know if i can help or test something.

@bdbch
Copy link
Contributor

bdbch commented Aug 22, 2022

Hey @nadar I'm a bit capped on time right now so I won't be able to fix this today since it looks like it's a bit deeper as it's also happening with React and Vue 3. Somehow on unmount/destroy, the last editor created gets destroyed instead of the one that should actually be deleted.

When I find time I will come back to this issue to see what exactly is happening and how we can fix this.

@nadar
Copy link
Author

nadar commented Aug 22, 2022

😞 ok, thanks. Any idea for a workaround? refs with an array and a random identifier? something likes this? Is there anything which acts as identifier? Or in dom?

@bdbch
Copy link
Contributor

bdbch commented Aug 22, 2022

Not for now as this issue is quite weird. I'll look into it as soon as I find a solution!
I marked it as high priority in our tracker.

@bdbch bdbch added this to the 2.0.0 milestone Aug 22, 2022
@bdbch bdbch removed this from the 2.0.0 milestone Sep 10, 2022
@oferitz
Copy link

oferitz commented Oct 3, 2022

We are seeing very similar issue with React, when you have multiple instances of the editor on the same page you get inconsistent mount/unmount behavior. it seems like change in one instance is affecting other instances.

@github-actions
Copy link
Contributor

This issue is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 7 days

@github-actions github-actions bot added the Info: Stale The issue or pullrequest has not been updated in a while and might be stale label Jan 14, 2023
@nadar
Copy link
Author

nadar commented Jan 14, 2023

No don't close, its a bug, which received the 2.0.0 milestone (and suddenly was removed again)

@github-actions github-actions bot removed the Info: Stale The issue or pullrequest has not been updated in a while and might be stale label Jan 15, 2023
@bdbch bdbch added this to the 2.0.0 milestone Jan 19, 2023
@subssn21
Copy link

Are there any workarounds for this issue? I am seeing it as well, and it is breaking things in a way that makes our app unusable.

@nadar
Copy link
Author

nadar commented Feb 22, 2023

Sadly they don't care :-) We have generated a super random :key='' value which seems to work.

@subssn21
Copy link

So I think this is not a bug (At least in Vue 3)

If you provide a key in your for loop the problem goes away
Reading the documentation in Vue 3 seems to define the exact behavior being shown, adding the key seems to solve it.
https://vuejs.org/guide/essentials/list.html#maintaining-state-with-key

@nadar
Copy link
Author

nadar commented Feb 22, 2023

we always used keys, but they might not been unique enough, the example uses the index as key. works everywhere, expect with tiptap.

@bdbch bdbch removed this from the 2.0.0 milestone Feb 23, 2023
@bdbch
Copy link
Contributor

bdbch commented Feb 24, 2023

We care, but we weren't able to find a good solution yet and have a few other things on our table so I wasn't able to work on this specific issue again.

If someone wants to investigate this feel free. I also think this is not related to Vue but how Editor instances are stored and rendered via the Renderer?

@bdbch bdbch added this to the 2.0.0 Release milestone Feb 24, 2023
@nadar
Copy link
Author

nadar commented Feb 24, 2023

@bdbch i am sorry, it was a inappropriate choice of words, it's still an opensource project and i should appreciate that. I am going to close this issue, since using f.e using uuidv4() keys as index works.

@nadar nadar closed this as completed Feb 24, 2023
@bdbch
Copy link
Contributor

bdbch commented Feb 24, 2023

No worries, didn't pick it up as an insult or something. Feel free to keep it open. I think it's a valid issue, but it's nice to keep information about your temporary solution in here so others can fix it on their end while we dig out a fix! :)