javascriptvue.jsvuejs3quilldelta

Error deleting text in QuillJS editor with Vue.js 3 : Uncaught TypeError: Cannot read properties of null (reading 'offset')


I initialized the QuillJS editor on a Vue.js 3 project (option API), and here's the code present in the component:

<script>
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import Quill from 'quill'

export default {
  name: 'AppChatContainer',
  components: { FontAwesomeIcon },
  props: {
    messages: {
      type: Array,
      required: true,
      default: () => []
    },
    isSending: {
      type: Boolean,
      default: false
    }
  },

  emits: ['new-message'],

  data() {
    return {
      quill: null
    }
  },

  mounted() {
    this.quill = new Quill('#message', {
      theme: 'bubble',
      modules: {
        toolbar: [
          ['bold', 'italic', { color: [] }],
          [{ list: 'ordered' }, { list: 'bullet' }]
        ]
      }
    })
  },

  methods: {
    handleSubmission() {
      const message = this.quill.getContents()
      this.$emit('new-message', message)
      // this.quill.setContents([{ insert: '\n' }]) // <= Error here
      // this.quill.setText('') // <= Error here
      this.quill.deleteText(0, this.quill.getLength()) // <= Error here
    },

    convertDeltaToHtml(delta) {
      const tempQuill = new Quill(document.createElement('div'))
      tempQuill.setContents(delta)
      return tempQuill.root.innerHTML
    }
  }
}
</script>

<template>
  <form class="h-100 position-relative" @submit.prevent="handleSubmission">
    <div class="flex-grow-1 overflow-y-scroll">
      <div
        v-for="message in messages"
        :key="message.id"
        :class="`message mb-4 ${message.isAuthor ? 'author' : ''} ${message.isSending ? 'sending' : ''}`"
      >
        <div class="content" v-html="convertDeltaToHtml(message.content)" />
        <small v-if="message.isSending" class="text-gray-700 fw-light d-block mt-1 ms-2 me-3">
          Envois en cours...
        </small>
        <small v-else class="text-gray-700 fw-light d-block mt-1 ms-2 me-3">
          {{ message.isAuthor ? 'Vous' : message.authorName }}, le {{ message.date }}</small
        >
      </div>
    </div>
    <div class="message-container">
      <div id="message" />
      <button type="submit" class="btn btn-amsom-green" :disabled="isSending">
        <font-awesome-icon v-if="!isSending" icon="paper-plane" />
        <div v-else class="spinner-border spinner-border-sm" role="status">
          <span class="visually-hidden">Envois en cours...</span>
        </div>
      </button>
    </div>
  </form>
</template>

<style scoped>
// ...
</style>

When I submit the form, handleSubmission() method is called. When I try to delete the editor content I have this error :

Uncaught TypeError: Cannot read properties of null (reading 'offset')
    at quill.js?v=b3144345:12600:26
    at Array.map (<anonymous>)
    at Proxy.normalizedToRange (quill.js?v=b3144345:12597:31)
    at Proxy.getRange (quill.js?v=b3144345:12586:25)
    at Proxy.update (quill.js?v=b3144345:12723:43)
    at Proxy.update (quill.js?v=b3144345:13796:20)
    at Proxy.getSelection (quill.js?v=b3144345:13690:10)
    at Proxy.modify (quill.js?v=b3144345:13906:44)
    at Proxy.deleteText (quill.js?v=b3144345:13553:19)
    at Proxy.handleSubmission (AppChatContainer.vue:47:18)

I've tried several ways of deleting content from the editor, but I always have the same problem...

handleSubmission() {
      const message = this.quill.getContents()
      this.$emit('new-message', message)
      // this.quill.setContents([{ insert: '\n' }])
      // this.quill.setText('');
      this.quill.deleteText(0, this.quill.getLength())
    },

Solution

  • Try this tiny wrapper component, you can build your editor upon it:

    npm add quill@2.0.0 vue-quilly
    # Or
    pnpm add quill@2.0.0 vue-quilly
    

    Import Quill full build if you need all modules or core build with minimum required modules:

    import Quill from 'quill' // Full build
    import Quill from 'quill/core' // Core build
    import { QuillyEditor } from 'vue-quilly'
    

    Add core styles. Also import one of Quill's themes, if you need one:

    import 'quill/dist/quill.core.css' // Required
    import 'quill/dist/quill.snow.css' // For snow theme (optional)
    import 'quill/dist/quill.bubble.css' // For bubble theme (optional)
    

    Define Quill options:

    const options = {
      theme: 'snow', // If you need Quill theme
      modules: {
        toolbar: true,
      },
      placeholder: 'Compose an epic...',
      readOnly: false
    }
    

    Initialize the editor:

    const editor = ref<InstanceType<typeof QuillyEditor>>()
    const model = ref<string>('<p>Hello Quilly!</p>')
    // Quill instance
    let quill: Quill | null = null
    onMounted(() => {
      quill = editor.value?.initialize(Quill)!
    })
    
    <QuillyEditor
      ref="editor"
      v-model="model"
      :options="options"
      @text-change="({ delta, oldContent, source }) => console.log('text-change', delta, oldContent, source)"
      @selection-change="({ range, oldRange, source }) => console.log('selection-change', range, oldRange, source)"
      @editor-change="(eventName) => console.log('editor-change', `eventName: ${eventName}`)"
      @focus="(quill) => console.log('focus', quill)"
      @blur="(quill) => console.log('blur', quill)"
      @ready="(quill) => console.log('ready', quill)"
    />
    

    Use v-model for HTML content type. You can set content in Delta format using Quill instance:

    quill?.setContents(
      new Delta()
        .insert('Hello')
        .insert('\n', { header: 1 })
        .insert('Some ')
        .insert('initial', { bold: true })
        .insert(' ')
        .insert('content', { underline: true })
        .insert('\n')
    )
    

    Creating editors with QullyEditor demo