primevue icon indicating copy to clipboard operation
primevue copied to clipboard

Editor: `v-model` not updating with Quill v2.0

Open FeBe95 opened this issue 10 months ago • 31 comments

Describe the bug

Quill v2.0 is now officially released (see https://github.com/quilljs/quill/releases). PrimeVue's docs state to simply run

npm install quill

to make the Editor component work. Displaying and editing the text inside the editor works just fine, but directly manipulating the v-model value does not. This issue was mentioned here as well:

  • #5381

I know that there are plans to replace Quill with a custom solution (mentioned in a discussion here), but until then a fix for this issue would be very appreciated.

Reproducer

https://stackblitz.com/edit/primevue-create-vue-issue-template-fsd4z9

PrimeVue version

3.51.0

Vue version

3.x

Language

ALL

Build / Runtime

Vite

Browser(s)

No response

Steps to reproduce the behavior

  1. Use the Editor component
  2. Set/update the v-model value

Expected behavior

The content of the Editor should change. It does not.

FeBe95 avatar Apr 17 '24 14:04 FeBe95

Same thing here. The editor works to write new texts. To edit something is not working

pedrodruviaro avatar Apr 19 '24 14:04 pedrodruviaro

Same thing here. The editor works to write new texts. To edit something is not working

Same issue.

jeverduzco avatar Apr 20 '24 02:04 jeverduzco

Same issue here and I couldn't figure out anything I could do even for a temporary fix.

kristuu avatar Apr 22 '24 10:04 kristuu

The same for me here, everyone that can help us, please give us some lights.

arikardnoir avatar Apr 22 '24 16:04 arikardnoir

In the meantime you can install quill 1.3.7. that's the latest non-2.0 version.

I just came to visit after seeing this in my console:

# npm audit report

quill  <=1.3.7
Severity: moderate
Cross-site Scripting in quill - https://github.com/advisories/GHSA-4943-9vgg-gr5r
fix available via `npm audit fix --force`
Will install [email protected], which is a breaking change

agm1984 avatar Apr 22 '24 19:04 agm1984

In the meantime you can install quill 1.3.7. that's the latest non-2.0 version.

I just came to visit after seeing this in my console:

# npm audit report

quill  <=1.3.7
Severity: moderate
Cross-site Scripting in quill - https://github.com/advisories/GHSA-4943-9vgg-gr5r
fix available via `npm audit fix --force`
Will install [email protected], which is a breaking change

That worked. However, we have the performance problem mentioned here: https://github.com/vueup/vue-quill/issues/409 But I think this is the best solution until this v-model problem is not resolved

pedrodruviaro avatar Apr 22 '24 19:04 pedrodruviaro

That worked. However, we have the performance problem mentioned here: vueup/vue-quill#409 But I think this is the best solution until this v-model problem is not resolved

Thanks, I did notice that in my project but I didn't debug it yet, so thats good to know.

agm1984 avatar Apr 22 '24 19:04 agm1984

In the meantime you can install quill 1.3.7. that's the latest non-2.0 version. I just came to visit after seeing this in my console:

# npm audit report

quill  <=1.3.7
Severity: moderate
Cross-site Scripting in quill - https://github.com/advisories/GHSA-4943-9vgg-gr5r
fix available via `npm audit fix --force`
Will install [email protected], which is a breaking change

That worked. However, we have the performance problem mentioned here: vueup/vue-quill#409 But I think this is the best solution until this v-model problem is not resolved

What you guys did? i cant understand, im already using quill 2.0.0, and still not working the getting the value

arikardnoir avatar Apr 22 '24 21:04 arikardnoir

What you guys did? i cant understand, im already using quill 2.0.0, and still not working the getting the value

I left quill at version 1.3.7 until v2 is supported.

$ npm uninstall --save quill
$ npm install --save quill@^1.3.7

agm1984 avatar Apr 22 '24 21:04 agm1984

What you guys did? i cant understand, im already using quill 2.0.0, and still not working the getting the value

I left quill at version 1.3.7 until v2 is supported.

$ npm uninstall --save quill
$ npm install --save quill@^1.3.7

Many thanks @agm1984, it works for me

arikardnoir avatar Apr 22 '24 23:04 arikardnoir

For reference: This is the corresponding issue (and PR) from primefaces/primereact repository. A fix for PrimeVue could be almost copy & paste, I guess:

  • https://github.com/primefaces/primereact/issues/5960
  • https://github.com/primefaces/primereact/pull/6429

FeBe95 avatar Apr 25 '24 15:04 FeBe95

Probably the best solution for this issue while using quill >= 2.0.0 is:

First, set a ref for the editor component and bind a method for the load event emitted by the component

<Editor ref="editor" v-model="form.description" @load="editorLoad">

So, after that, create a method for being executed after editor load:

editorLoad({instance}) {
    const delta = this.$refs.editor.quill.clipboard.convert({ html: "<p>your html goes here!</p>" });
    this.$refs.editor.quill.setContents(delta, 'silent');
},

LeonardoRochaInacio avatar May 03 '24 18:05 LeonardoRochaInacio

Probably the best solution for this issue while using quill >= 2.0.0 is:

First, set a ref for the editor component and bind a method for the load event emitted by the component

<Editor ref="editor" v-model="form.description" @load="editorLoad">

So, after that, create a method for being executed after editor load:

editorLoad({instance}) {
    const delta = this.$refs.editor.quill.clipboard.convert({ html: "<p>your html goes here!</p>" });
    this.$refs.editor.quill.setContents(delta, 'silent');
},

this worked but also had to call my editorLoad function inside a watch callback, also needed to use nextTick to get this working properly.

const editorLoad = () => {
  const newValue = isEditing.value || isCreating.value
      ? editingValue.value
      : selectedValue.value;
  const delta = editor.value.quill.clipboard.convert({
    html: newValue || ""
  });
  nextTick(() => editor.value.quill.setContents(delta));
};
watch([selectedValue, isEditing, isCreating], editorLoad);

visgotti avatar May 29 '24 00:05 visgotti

Using script setup

const editorRef = ref()

watch(editorRef, (editor) => {
  if (!editor) return
  // Hack needed for Quill v2: https://github.com/primefaces/primevue/issues/5606#issuecomment-2093536386
  editor.renderValue = function renderValue(value) {
    if (this.quill) {
      if (value) {
        const delta = this.quill.clipboard.convert({ html: value })
        this.quill.setContents(delta, 'silent')
      } else {
        this.quill.setText('')
      }
    }
  }.bind(editor) // Bind needed for production build
})
<Editor ref="editorRef" />

tfoxkiu avatar Jun 03 '24 16:06 tfoxkiu

Using script setup

watch(editorRef, (editor) => {
  if (!editor) return
  // Hack needed for Quill v2: https://github.com/primefaces/primevue/issues/5606#issuecomment-2093536386
  editor.renderValue = function renderValue(value) {
    if (this.quill) {
      if (value) {
        const delta = this.quill.clipboard.convert({ html: value })
        this.quill.setContents(delta, 'silent')
      } else {
        this.quill.setText('')
      }
    }
  }.bind(editor) // Bind needed for production build
})
<Editor ref="editorRef" />

worked for me , using script setup . I just replace value with postItem.value.content, where postItem is also a ref

taking a new value when the component is mounted :

<script setup>
 // pinia store
 import { usePostStore } from '@/stores/post' 

 let postItem = ref({
   id: '',
   title: '',
   content: '',
 })

onMounted(() => {
 const postStore = usePostStore()
 const postToEdit = postStore.postInPage

 postItem.value.id = postToEdit._doc.id
 postItem.value.title = postToEdit._doc.title
 postItem.value.content = postToEdit._doc.content
})

watch(editorRef, (editor) => {
 if (!editor) return
   editor.renderValue = function renderValue(value) {
     if (this.quill) {
       if (postItem.value.content) {
         const delta = this.quill.clipboard.convert({ html: postItem.value.content })
         this.quill.setContents(delta, 'silent')
       } else {
         this.quill.setText('')
      }
  }
 }.bind(editor) // Bind needed for production build
})
</script>

BeinRain06 avatar Jun 13 '24 16:06 BeinRain06

You can define a new customized component 'MyEditor':

<script setup>
import Editor from "primevue/editor"

const props = defineProps({
	modelValue: {
		type: String,
		default: ""
	}
})

const emits = defineEmits(["update:modelValue"])

const onLoad = ({instance}) => {
	instance.setContents(instance.clipboard.convert({
		html: props.modelValue
	}))
}

const onChange = (v) => {
	emits("update:modelValue", v)
}
</script>

<template>
	<Editor editorStyle="height: 20em" @load="onLoad" @update:modelValue="onChange"></template>
	</Editor>
</template>

works well with "quill": "^2.0.2"

iwind avatar Jun 30 '24 05:06 iwind

You can define a new customized component 'MyEditor':

<script setup>
import Editor from "primevue/editor"

const props = defineProps({
	modelValue: {
		type: String,
		default: ""
	}
})

const emits = defineEmits(["update:modelValue"])

const onLoad = ({instance}) => {
	instance.setContents(instance.clipboard.convert({
		html: props.modelValue
	}))
}

const onChange = (v) => {
	emits("update:modelValue", v)
}
</script>

<template>
	<Editor editorStyle="height: 20em" @load="onLoad" @update:modelValue="onChange"></template>
	</Editor>
</template>

works well with "quill": "^2.0.2"

This worked perfect. Thank you so much!

tan-wood avatar Jul 01 '24 19:07 tan-wood

Based on my previous answer, an easier way is to patch renderValue in the component definition.

import Editor from 'primevue/editor';

// Fix needed for Quill v2: https://github.com/primefaces/primevue/issues/5606#issuecomment-2203975395
Editor.methods.renderValue = function renderValue(value) {
  if (this.quill) {
    if (value) {
      const delta = this.quill.clipboard.convert({ html: value });
      this.quill.setContents(delta, 'silent');
    } else {
      this.quill.setText('');
    }
  }
};

In TypeScript:

import Editor from 'primevue/editor';

// Fix needed for Quill v2: https://github.com/primefaces/primevue/issues/5606#issuecomment-2203975395
;(Editor as any).methods.renderValue = function renderValue(
  this: { quill?: Quill },
  value: string,
) {
  if (this.quill) {
    if (value) {
      const delta = this.quill.clipboard.convert({ html: value })
      this.quill.setContents(delta, 'silent')
    } else {
      this.quill.setText('')
    }
  }
}

This needs to be executed only once and it will apply to every instance of Editor. So you can just use <Editor /> and it will work. This has the following advantages:

  • There's no need to apply the fix every time you want to use Editor.
  • There's no need to create a wrapper, which would lose the autocompletion/typing and it's harder to implement if you want all the props and events of Editor.

tfoxkiu avatar Jul 02 '24 17:07 tfoxkiu

If you want to be able to use Quill 2.0+ with , TypeScript and have access to all props:

@/components/TextEditor.vue

<template>
  <Editor v-bind="props" @load="onLoad" @update:modelValue="onChange" />
</template>

<script setup lang="ts">
import Editor, { type EditorLoadEvent, type EditorProps } from 'primevue/editor';

const props = defineProps<EditorProps>();

const emits = defineEmits(['update:modelValue']);

const onLoad = ({ instance }: EditorLoadEvent) => {
  instance.setContents(
    instance.clipboard.convert({
      html: props.modelValue
    })
  );
};

const onChange = (v: string) => {
  emits('update:modelValue', v);
};
</script>

reqwire avatar Jul 03 '24 16:07 reqwire

What you guys did? i cant understand, im already using quill 2.0.0, and still not working the getting the value

I left quill at version 1.3.7 until v2 is supported.

$ npm uninstall --save quill
$ npm install --save quill@^1.3.7

@agm1984 Thank you so much bro, it works for me

Hudhaifa-1 avatar Jul 23 '24 15:07 Hudhaifa-1

This Chrome deprecation has taken effect two days ago (23rd July) and since then I've started seeing this error on any pages with the editor component: [Deprecation] Listener added for a 'DOMNodeInserted' mutation event. Support for this event type has been removed, and this event will no longer be fired.

This is visible in the console logs of the official site as well.

It looks like this is caused by quill v1 :fearful: (I'm not sure if quill v2 resolves this).


Edit: I see that the deprecation is indeed addressed in v2, as per the linked issue: https://github.com/primefaces/primevue/issues/5381#issuecomment-1999956454

francois-launchbase avatar Jul 25 '24 16:07 francois-launchbase

You can define a new customized component 'MyEditor':

<script setup>
import Editor from "primevue/editor"

const props = defineProps({
	modelValue: {
		type: String,
		default: ""
	}
})

const emits = defineEmits(["update:modelValue"])

const onLoad = ({instance}) => {
	instance.setContents(instance.clipboard.convert({
		html: props.modelValue
	}))
}

const onChange = (v) => {
	emits("update:modelValue", v)
}
</script>

<template>
	<Editor editorStyle="height: 20em" @load="onLoad" @update:modelValue="onChange"></template>
	</Editor>
</template>

works well with "quill": "^2.0.2"

Funcionou certinho!! Obrigadoo

Tive um pequeno desafio pois meu componente não inicializava com o conteúdo pois a página onde ele estava inserido realizava uma requisição à API, daí para garantir o carregamento, apliquei o seguinte código:

<template>
        <Editor ref="editorRef" editorStyle="height: 250px" @load="onLoad" @update:modelValue="onChange"></Editor>
</template>

<script setup>
const updateEditorContent = (content) => {
	if (editorRef.value) {
		const editor = editorRef.value.quill;
		editor.setContents(editor.clipboard.convert({
			html: content
		}))
	}
}

watch(() => props.modelValue, (newValue) => {
	updateEditorContent(newValue)
}, { immediate: true })
</script>

matlimaaa avatar Jul 31 '24 23:07 matlimaaa

I just updated PrimeVue to v4 and got this issue when I bumped up Quill to v2.

This problem is still happening.

agm1984 avatar Aug 01 '24 19:08 agm1984

If you want to be able to use Quill 2.0+ with , TypeScript and have access to all props:

@/components/TextEditor.vue

<template>
  <Editor v-bind="props" @load="onLoad" @update:modelValue="onChange" />
</template>

<script setup lang="ts">
import Editor, { type EditorLoadEvent, type EditorProps } from 'primevue/editor';

const props = defineProps<EditorProps>();

const emits = defineEmits(['update:modelValue']);

const onLoad = ({ instance }: EditorLoadEvent) => {
  instance.setContents(
    instance.clipboard.convert({
      html: props.modelValue
    })
  );
};

const onChange = (v: string) => {
  emits('update:modelValue', v);
};
</script>

Funcionou perfeitamente aqui, muito obrigado pela ajuda.

douglas73 avatar Aug 11 '24 01:08 douglas73

Hi everyone,

I updated the Quill version from 1.3.7 to 2.0.2, and after the update, I noticed that when opening a record to update the value in the text editor, the editor wasn't picking up the existing values correctly. I found a solution in this conversation and implemented it in my project. Now, everything is working as expected.

I created a separate file for the text editor component:

ui-component/text-editor.vue

`

`

Then, I simply called this component and passed the required value:

@agm1984 hope this will help you.

haseeb058 avatar Sep 10 '24 14:09 haseeb058

Hi there,

For the ones who use vue3 or nuxt3 with primevue v3 and quill 2.0.2, this solution properly works during init and updates:

<template>
	<Editor ref="editorRef" :editorStyle="`height: ${size};`" @load="onLoad" @update:modelValue="onChange">
		<template v-slot:toolbar>
			<span class="ql-formats">

				<select class="ql-header">
					<option value="1"></option>
					<option value="2"></option>
					<option selected></option> <!-- Default normal text -->
				</select>
				<button class="ql-bold"></button>
				<button class="ql-italic"></button>
				<button class="ql-underline"></button>
				<button class="ql-strike"></button>

				<button class="ql-align"></button>
				<button class="ql-list" value="ordered"></button>
				<button class="ql-list" value="bullet"></button>

				<select class="ql-color"></select>
				<select class="ql-background"></select>

				<button class="ql-link"></button>
				<button class="ql-image"></button>

			</span>
		</template>
	</Editor>
</template>

<script setup>
const props = defineProps({
	modelValue: String,
	size: String
})

const emits = defineEmits(["update:modelValue"])

const editorRef = ref();


const textValue = ref();

const updateEditorContent = () => {
	if (editorRef.value && editorRef.value.quill) {
		const editor = editorRef.value.quill;
		const currentContent = editor.root.innerHTML; // Get the current HTML content
		// Update only if modelValue differs from editor's current content
		if (currentContent !== props.modelValue) {
			const delta = editor.clipboard.convert({ "html": props.modelValue}); // Convert the HTML to Quill Delta
			editor.setContents(delta);
		}
	}
}


// Handle editor content change
const onChange = (v) => {
	emits("update:modelValue", v)
}

watch(() => props.modelValue, () => {
	 updateEditorContent();
})


const onLoad = () => {
	updateEditorContent();
}
</script>

Using it this way:

<myEditor key="lefttext" :modelValue="leftText" size="100%" @update:modelValue="setLeftText"/>

Will be nice to add it to primevue 3...

MathieuB1 avatar Sep 19 '24 19:09 MathieuB1

Hi there. When I install latest quill version then the bullet list option in toolbar adds ol tags instead of ul tags. In 1.3.7 version it works correctly. What is the workaround since the 1.3.7 is not safe to use. Thanks in advance

stefdomandtom avatar Oct 01 '24 16:10 stefdomandtom