primefaces / primevue

Next Generation Vue UI Component Library
https://primevue.org
MIT License
10.47k stars 1.22k forks source link

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

Open FeBe95 opened 6 months ago

FeBe95 commented 6 months ago

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.

pedrodruviaro commented 6 months ago

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

jeverduzco commented 6 months ago

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

Same issue.

kristuu commented 6 months ago

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

arikardnoir commented 6 months ago

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

agm1984 commented 6 months ago

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 commented 6 months ago

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: https://github.com/vueup/vue-quill/issues/409 But I think this is the best solution until this v-model problem is not resolved

agm1984 commented 6 months ago

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 commented 6 months ago

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 commented 6 months ago

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 commented 6 months ago

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 commented 6 months ago

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 commented 6 months ago

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 commented 5 months ago

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 commented 5 months ago

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 commented 4 months ago

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 commented 4 months ago

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 commented 4 months ago

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 commented 4 months ago

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:

reqwire commented 4 months ago

If you want to be able to use Quill 2.0+ with

`

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

@agm1984 hope this will help you.

MathieuB1 commented 1 month ago

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 commented 1 month ago

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 commented 3 weeks ago

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 commented 3 weeks ago

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 commented 3 weeks ago

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 commented 3 weeks ago

zahedur

Thank you very much, the problem is solved

hamzehparsi commented 1 week ago

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

devtopher commented 1 week ago

If you want to be able to use Quill 2.0+ with Githubissues.

  • Githubissues is a development platform for aggregating issues.