Search code examples
javascriptvue.jsvuejs2tiptap

How to bind content of Tiptap editor?


I'm using the Tiptap editor and I have issues accessing the content of the editor.

I need to post the content of the editor to an API so I need the content. Here is my code:

import tippy from "tippy.js";
import { Editor, EditorContent, VueRenderer } from "@tiptap/vue-2";
import Document from "@tiptap/extension-document";
import Paragraph from "@tiptap/extension-paragraph";
import Text from "@tiptap/extension-text";

export default {
    components: {
        EditorContent
    },

    props: {
        comment_type: String,
        comment_text: String,
        comment_id: Number,
        edit_mode: Boolean
    },

    data() {
        return {
            editor: null,
            limit: 280,
            users: [],
            comment_da: {},
            edit_comment_da: {}
        };
    },
    
    watch: {
        comment_text(value) {
            console.log(value);
            this.editor.setContent(value);
        }
    },

    mounted() {
        const self = this;
        const CustomParagraph = Paragraph.extend({
            addKeyboardShortcuts() {
                return {
                    // ↓ your new keyboard shortcut
                    "Shift-Enter": () => self.addComment()
                };
            }
        });
        this.editor = new Editor({
            extensions: [
                Document,
                CustomParagraph,
                Text,
            ],
            content: this.comment_text
        });
    },

    beforeDestroy() {
        this.editor.destroy();
    },
  }
<template>
  <div>
    <editor-content :editor="editor" class="form-control"/>
  </div>
</template>

In the parent component I have some properties which I need to pass to the Tiptap editor:

import editor from "./components/editor";

new Vue({
  components: {
        editor
    },
    data() {
      comment_da: {},
      comment_text: "",
    }
})
<div class="m-messenger__form-controls">
    <editor 
        v-model="comment_text"
        :comment_text="comment_text"
        :comment_type="'comment'"
        :comment_id="comment_da.id"
    />
</div>

I cannot access the editor content. I've tried this solution but I'm getting this error constantly while typing in it:

Error: "getHTML is not a function"


Solution

  • I found the answer in the Tiptap documentation:

    import editor from "./components/editor";
    
    new Vue({
      components: {
            editor
        },
        data() {
          comment_da: {},
          comment_text: "",
        }
    })
    <div class="m-messenger__form-controls">
        <editor 
            v-model="comment_text"
            :comment_text="comment_text"
            :comment_type="'comment'"
            :comment_id="comment_da.id"
        />
    </div>

    import tippy from "tippy.js";
    import { Editor, EditorContent, VueRenderer } from "@tiptap/vue-2";
    import Document from "@tiptap/extension-document";
    import Paragraph from "@tiptap/extension-paragraph";
    import Text from "@tiptap/extension-text";
    
    export default {
        components: {
            EditorContent
        },
    
        props: {
            comment_type: String,
            comment_text: String,
            comment_id: Number,
            edit_mode: Boolean
        },
    
        data() {
            return {
                editor: null,
                limit: 280,
                users: [],
                comment_da: {},
                edit_comment_da: {}
            };
        },
        
        watch: {
            comment_text(value) {
                console.log(value);
                this.editor.setContent(value);
            }
        },
    
        mounted() {
            const self = this;
            const CustomParagraph = Paragraph.extend({
                addKeyboardShortcuts() {
                    return {
                        // ↓ your new keyboard shortcut
                        "Shift-Enter": () => self.addComment()
                    };
                }
            });
            this.editor = new Editor({
                extensions: [
                    Document,
                    CustomParagraph,
                    Text,
                    Mention.configure({
                        HTMLAttributes: {
                            class: "mention"
                        },
                        suggestion: {
                            items: query => {
                                var self = this;
                                const search_user = new crud(
                                    "/chat/mention/" + query
                                );
                                search_user.get_all_data(true, false, data => {
                                    self.users = data.data;
                                });
                                return this.users.filter(item =>
                                    item.name
                                        .toLowerCase()
                                        .startsWith(query.toLowerCase())
                                );
                            },
                            render: () => {
                                let component;
                                let popup;
    
                                return {
                                    onStart: props => {
                                        component = new VueRenderer(MentionList, {
                                            parent: this,
                                            propsData: props
                                        });
    
                                        popup = tippy("body", {
                                            getReferenceClientRect:
                                                props.clientRect,
                                            appendTo: () => document.body,
                                            content: component.element,
                                            showOnCreate: true,
                                            interactive: true,
                                            trigger: "manual",
                                            placement: "bottom-start"
                                        });
                                    },
                                    onUpdate(props) {
                                        component.updateProps(props);
    
                                        popup[0].setProps({
                                            getReferenceClientRect: props.clientRect
                                        });
                                    },
                                    onKeyDown(props) {
                                        return component.ref?.onKeyDown(props);
                                    },
                                    onExit() {
                                        popup[0].destroy();
                                        component.destroy();
                                    }
                                };
                            }
                        }
                    })
                ],
                content: this.comment_text,
                onUpdate() {
                    // You can access to both HTML and JSON type of your content
                    const json = this.getJSON();
                    const html = this.getHTML();
                    // send the content to an API here
                    this.comment_text = json.content[0].content[0].text
                        ? json.content[0].content[0].text
                        : "";
                }
            });
        },
    
        beforeDestroy() {
            this.editor.destroy();
        },
      }
    <template>
      <div>
        <editor-content :editor="editor" class="form-control"/>
      </div>
    </template>

    If you are curious, you can take a look at onUpdate part of my code for get the changes.