[Vuejs]-How to bind two Quill Editor instances to different v-models?

0👍

I suspect it has to do with the DOM manipulation done in quill.js, which conflicts with Vue’s own management of the virtual DOM.

However, I was able to resolve the issue by implementing the following optimizations.

Optimizations

You can significantly reduce the duplication of code and have a single <quill-editor> by conditionally setting store.re.body/store.re.descr based on props.description:

<template>
  <div id="text-editor" class="text-editor">
    <quill-editor ref="quill" :modules="modules" :toolbar="toolbar" v-model:content="content" contentType="html" />
  </div>
</template>

<script setup>
⋮
const props = defineProps({
  description: Boolean,
})
      👇
if (props.description) {
  store.re.descr = ''
} else {
  store.re.body = ''
}

watch(content, newValue => {
  newContent = newValue
        👇
  if (props.description) {
    store.re.descr = newValue
  } else {
    store.re.body = newValue
  }
})

watch(
  () => props.description ? store.re.descr : store.re.body, 👈
  newValue => {⋯}
)
⋮
</script>

demo 1

But this solution does not scale well if you later need to have the editor update more store properties (e.g., store.re.title and store.re.subtitle). A better solution would be to make the editor update any given property via a v-model implementation:

  1. Define a modelValue string prop (via defineProps).

  2. Define an update:modelValue emit with a string value (via defineEmits).

  3. In the watch that sets store.re.body/store.re.descr, emit the update:modelValue event with new value.

  4. In the watch for store.re.body/store.re.descr, change the watch’s source to be props.modelValue.

<script setup>
⋮
1️⃣
const props = defineProps({
  modelValue: String,
})
2️⃣
const emit = defineEmits({
  'update:modelValue': value => true,
})

watch(content, newValue => {
  newContent = newValue
              3️⃣
  emit('update:modelValue', newValue)
})

watch(
  () => props.modelValue, 4️⃣
  newValue => {⋯}
)
⋮
</script>

Then in the parent component, use v-model to bind store.re.body/store.re.descr:

<text-editor v-model="store.re.descr" />
<text-editor v-model="store.re.body" />

For some reason, setting store.re.body and store.re.descr in the parent component does not update the v-model in this case, but you can workaround that by using a computed prop. To make the changes to store.re.body reactive in this example:

<template>
  <text-editor v-model="body" />
</template>

<script setup>
import { computed } from 'vue'

const body = computed({
  get: () => store.re.body,
  set: v => store.re.body = v,
})
</script>

demo 2

Leave a comment