I'm trying to pass data to a custom vue component rendered inside the tiptap editor
. I can pass the default
attribute, but assigning it a reactive value doesn't seem to work.
This is tiptap-node-extension.js
File:
import {Node, mergeAttributes} from '@tiptap/core' import {VueNodeViewRenderer} from '@tiptap/vue-3' import Component from '@/views/components/vue-component.vue' export default Node.create({ parseHTML() { return [{ tag: 'vue-component' }] }, renderHTML({ HTMLAttributes }) { return ['vue-component', mergeAttributes(HTMLAttributes)] }, addNodeView() { return VueNodeViewRenderer(Component) }, })
editor
The script setup
part of the component:
<script setup> import {useEditor, EditorContent, BubbleMenu} from '@tiptap/vue-3' import StarterKit from '@tiptap/starter-kit' import {Underline} from "@tiptap/extension-underline"; import {TextAlign} from "@tiptap/extension-text-align"; import {Link} from "@tiptap/extension-link"; import VueComponent from '@/js/tiptap-node-extension.js' const editor = useEditor({ extensions: [ StarterKit, TextAlign.configure({ types: ['heading', 'paragraph'] }), Underline, Link, VueComponent.extend({ name: 'vueComponent', group: 'block', draggable: true, addAttributes() { return { src: { default: '123', } } }, } ), ], content: props.modelValue, onUpdate: ({ editor }) => { emit('update:modelValue', editor.getHTML()) }, editable: props.locked ? false : store.admin }) const sendDataToExtension = async (editor, event) => { // Triggered upon event ... state.src = '123' editor.chain().focus().insertContent('<vue-component/>').run() } </script>
and vue components
:
<script setup> import {NodeViewWrapper} from '@tiptap/vue-3' const props = defineProps({ node: { type: Object, required: true }, updateAttributes: { type: Function, required: true, } }) </script> <template> <node-view-wrapper class="vue-component" data-drag-handle=""> <p>{{ node.attrs.src }}</p> </node-view-wrapper> </template>The
default for
src
passes, but when I try to allocate a response object (created after installing the editor
component) it ends up becoming Become undefined
.
This works:
src: { default: '123' }
But this is not:
... src: { default: state.src } ... const sendDataToExtension = async (editor, event) => { // triggered upon event ... state.src = '123' editor.chain().focus().insertContent('<vue-component/>').run() }
How to send data to the vue component
created after mounting editor
?
try:
editor.chain().focus().insertContent('<vue-component/>', {src: state.src}).run()
P粉3347213592023-12-27 00:43:54
First of all, I would recommend creating a purpose-built extension instead of using the generic VueComponent
as you do now. If you build more extensions based on that extension, you will have multiple extensions competing for that tag. Move all the code you set in the extension into the actual extension and you can set any tag name you want.
Now I think the problem is here: insertContent
It looks like this:
insertContent: (value: Content, options?: { parseOptions?: ParseOptions; updateSelection?: boolean; })
Content declared as
export declare type Content = HTMLContent | JSONContent | JSONContent[] | null; export declare type HTMLContent = string; export declare type JSONContent = { type?: string; attrs?: Record<string, any>; content?: JSONContent[]; marks?: { type: string; attrs?: Record<string, any>; [key: string]: any; }[]; text?: string; [key: string]: any; };
In your case you have to add the src attribute to your html string, but I recommend you to use JSONContent
Type in your case:
editor.chain().focus().insertContent({type: "vueComponent", attrs:{src: state.src}}).run()
The type here is the component name you set.
Hope this makes sense and the documentation on tiptap is goodhttps:// /tiptap.dev/guide/custom-extensions/#attributes If you have any further questions please let me know.