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

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

Closed
FeBe95 opened this issue Apr 17, 2024 · 34 comments
Closed

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

FeBe95 opened this issue Apr 17, 2024 · 34 comments
Labels
Type: Bug Issue contains a bug related to a specific component. Something about the component is not working
Milestone

Comments

@FeBe95
Copy link

FeBe95 commented Apr 17, 2024

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:

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 FeBe95 added the Status: Needs Triage Issue will be reviewed by Core Team and a relevant label will be added as soon as possible label Apr 17, 2024
@pedrodruviaro
Copy link

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

@jeverduzco
Copy link

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

Same issue.

@kristuu
Copy link

kristuu commented Apr 22, 2024

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

@arikardnoir
Copy link

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

@agm1984
Copy link

agm1984 commented Apr 22, 2024

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 quill@2.0.0, which is a breaking change

@pedrodruviaro
Copy link

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 quill@2.0.0, 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

@agm1984
Copy link

agm1984 commented Apr 22, 2024

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.

@arikardnoir
Copy link

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 quill@2.0.0, 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

@agm1984
Copy link

agm1984 commented Apr 22, 2024

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

@arikardnoir
Copy link

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

@FeBe95
Copy link
Author

FeBe95 commented Apr 25, 2024

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

@LeonardoRochaInacio
Copy link

LeonardoRochaInacio commented May 3, 2024

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');
},

@visgotti
Copy link

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);

@tfoxkiu
Copy link

tfoxkiu commented Jun 3, 2024

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" />

@BeinRain06
Copy link

BeinRain06 commented Jun 13, 2024

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>

@iwind
Copy link

iwind commented Jun 30, 2024

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"

@tan-wood
Copy link

tan-wood commented Jul 1, 2024

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!

@tfoxkiu
Copy link

tfoxkiu commented Jul 2, 2024

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.

@reqwire
Copy link

reqwire commented Jul 3, 2024

If you want to be able to use Quill 2.0+ with <script setup>, 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>

@Hudhaifa-1
Copy link

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

@francois-launchbase
Copy link

francois-launchbase commented Jul 25, 2024

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 😨 (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: #5381 (comment)

@matlimaaa
Copy link

matlimaaa commented Jul 31, 2024

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>

@agm1984
Copy link

agm1984 commented Aug 1, 2024

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

This problem is still happening.

@douglas73
Copy link

If you want to be able to use Quill 2.0+ with <script setup>, 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.

@haseeb058
Copy link

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

`


<Editor v-model="localModelValue" @update:modelValue="changeNotificationQuillEditor" editorStyle="height: 20em" @load="onLoad" />

<script lang="ts"> import { defineComponent, ref, PropType } from 'vue'; import Editor from 'primevue/editor'; export default defineComponent({ name: 'TextEditor', props: { modelValue: { type: String, required: true }, changeNotificationQuillEditor: { type: Function as PropType<(value: string) => void>, required: true }, }, components: { Editor }, setup(props: any) { const localModelValue = ref(props.modelValue); const onLoad = ({ instance }: any) => { instance.setContents(instance.clipboard.convert({ html: props.modelValue })); }; return { onLoad, localModelValue }; } }); </script>

`

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

@agm1984 hope this will help you.

@MathieuB1
Copy link

MathieuB1 commented Sep 19, 2024

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...

@stefdomandtom
Copy link

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

@hamzehparsi
Copy link

In Nuxt version 3.13.2 with Nitro 2.9.7, the Quill option version 2.0.2 does not work with v-model and cannot render the content editor inside itself.

@tan-wood
Copy link

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

I've had this issue as well

@zahedur
Copy link

zahedur commented Oct 10, 2024

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.

It's worked, thank you

@hamzehparsi
Copy link

zahedur

Thank you very much, the problem is solved

@hamzehparsi
Copy link

“I wish they would solve this issue within the component itself so that this code wouldn’t be necessary.”

@devtopher
Copy link

If you want to be able to use Quill 2.0+ with <script setup>, 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>

This worked for me. Thank you

@devtopher
Copy link

devtopher commented Nov 5, 2024

If anyone else is having problems editing links with this, i.e you add a link, then go back and edit it, if Quill is clearing your text, it's because setContents is getting called and deleting the text even if your model value persists.

This is what we did to "fix" that.

 const onLoad = ({ instance }: EditorLoadEvent) => {
    instance.setContents(
      instance.clipboard.convert({
        html: props.modelValue
      })
    );
    instance.setContents = () => {
      // we replace this function with nothing so it stops breaking editing links
    };
  };

@tugcekucukoglu tugcekucukoglu added Type: Bug Issue contains a bug related to a specific component. Something about the component is not working and removed Status: Needs Triage Issue will be reviewed by Core Team and a relevant label will be added as soon as possible labels Nov 13, 2024
@tugcekucukoglu tugcekucukoglu added this to the 4.2.2 milestone Nov 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Bug Issue contains a bug related to a specific component. Something about the component is not working
Projects
None yet
Development

No branches or pull requests