mirror of
https://github.com/ueberdosis/tiptap.git
synced 2024-12-26 10:47:49 +08:00
d31e49b03a
* skip 'like that' when we don't need it anyway * change to 'like this' because it is before the example instead of after
230 lines
5.7 KiB
Markdown
230 lines
5.7 KiB
Markdown
---
|
||
tableOfContents: true
|
||
---
|
||
|
||
# Node views with Vue
|
||
|
||
## Introduction
|
||
Using Vanilla JavaScript can feel complex if you are used to work in Vue. Good news: You can use regular Vue components in your node views, too. There is just a little bit you need to know, but let’s go through this one by one.
|
||
|
||
## Render a Vue component
|
||
Here is what you need to do to render Vue components inside your editor:
|
||
|
||
1. [Create a node extension](/guide/custom-extensions)
|
||
2. Create a Vue component
|
||
3. Pass that component to the provided `VueNodeViewRenderer`
|
||
4. Register it with `addNodeView()`
|
||
5. [Configure Tiptap to use your new node extension](/guide/configuration)
|
||
|
||
This is how your node extension could look like:
|
||
|
||
```js
|
||
import { Node } from '@tiptap/core'
|
||
import { VueNodeViewRenderer } from '@tiptap/vue-2'
|
||
import Component from './Component.vue'
|
||
|
||
export default Node.create({
|
||
// configuration …
|
||
|
||
addNodeView() {
|
||
return VueNodeViewRenderer(Component)
|
||
},
|
||
})
|
||
```
|
||
|
||
There is a little bit of magic required to make this work. But don’t worry, we provide a wrapper component you can use to get started easily. Don’t forget to add it to your custom Vue component, like shown below:
|
||
|
||
```html
|
||
<template>
|
||
<node-view-wrapper>
|
||
Vue Component
|
||
</node-view-wrapper>
|
||
</template>
|
||
```
|
||
|
||
Got it? Let’s see it in action. Feel free to copy the below example to get started.
|
||
|
||
https://embed.tiptap.dev/preview/GuideNodeViews/VueComponent
|
||
|
||
That component doesn’t interact with the editor, though. Time to wire it up.
|
||
|
||
## Access node attributes
|
||
The `VueNodeViewRenderer` which you use in your node extension, passes a few very helpful props to your custom Vue component. One of them is the `node` prop. Add this snippet to your Vue component to directly access the node:
|
||
|
||
```js
|
||
props: {
|
||
node: {
|
||
type: Object,
|
||
required: true,
|
||
},
|
||
},
|
||
```
|
||
|
||
That enables you to access node attributes in your Vue component. Let’s say you have [added an attribute](/guide/custom-extensions#attributes) named `count` to your node extension (like we did in the above example) you could access it like this:
|
||
|
||
```js
|
||
this.node.attrs.count
|
||
```
|
||
|
||
## Update node attributes
|
||
You can even update node attributes from your node, with the help of the `updateAttributes` prop passed to your component. Just add this snippet to your component:
|
||
|
||
```js
|
||
props: {
|
||
updateAttributes: {
|
||
type: Function,
|
||
required: true,
|
||
},
|
||
},
|
||
```
|
||
|
||
Pass an object with updated attributes to the function:
|
||
|
||
```js
|
||
this.updateAttributes({
|
||
count: this.node.attrs.count + 1,
|
||
})
|
||
```
|
||
|
||
And yes, all of that is reactive, too. A pretty seamless communication, isn’t it?
|
||
|
||
## Adding a content editable
|
||
There is another component called `NodeViewContent` which helps you adding editable content to your node view. Here is an example:
|
||
|
||
```html
|
||
<template>
|
||
<node-view-wrapper class="dom">
|
||
<node-view-content class="content-dom" />
|
||
</node-view-wrapper>
|
||
</template>
|
||
|
||
<script>
|
||
import { NodeViewWrapper, NodeViewContent } from '@tiptap/vue-2'
|
||
|
||
export default {
|
||
components: {
|
||
NodeViewWrapper,
|
||
NodeViewContent,
|
||
},
|
||
}
|
||
</script>
|
||
```
|
||
|
||
You don’t need to add those `class` attributes, feel free to remove them or pass other class names. Try it out in the following example:
|
||
|
||
https://embed.tiptap.dev/preview/GuideNodeViews/VueComponentContent
|
||
|
||
Keep in mind that this content is rendered by Tiptap. That means you need to tell what kind of content is allowed, for example with `content: 'inline*'` in your node extension (that’s what we use in the above example).
|
||
|
||
The `NodeViewWrapper` and `NodeViewContent` components render a `<div>` HTML tag (`<span>` for inline nodes), but you can change that. For example `<node-view-content as="p">` should render a paragraph. One limitation though: That tag must not change during runtime.
|
||
|
||
## All available props
|
||
For advanced use cases, we pass a few more props to the component.
|
||
|
||
### editor
|
||
The editor instance.
|
||
|
||
### node
|
||
Access the current node.
|
||
|
||
### decorations
|
||
An array of decorations.
|
||
|
||
### selected
|
||
`true` when there is a `NodeSelection` at the current node view.
|
||
|
||
### extension
|
||
Access to the node extension, for example to get options.
|
||
|
||
### getPos()
|
||
Get the document position of the current node.
|
||
|
||
### updateAttributes()
|
||
Update attributes of the current node.
|
||
|
||
### deleteNode()
|
||
Delete the current node.
|
||
|
||
Here is the full list of what props you can expect:
|
||
|
||
```html
|
||
<template>
|
||
<node-view-wrapper />
|
||
</template>
|
||
|
||
<script>
|
||
import { NodeViewWrapper } from '@tiptap/vue-2'
|
||
|
||
export default {
|
||
components: {
|
||
NodeViewWrapper,
|
||
},
|
||
|
||
props: {
|
||
// the editor instance
|
||
editor: {
|
||
type: Object,
|
||
},
|
||
|
||
// the current node
|
||
node: {
|
||
type: Object,
|
||
},
|
||
|
||
// an array of decorations
|
||
decorations: {
|
||
type: Array,
|
||
},
|
||
|
||
// `true` when there is a `NodeSelection` at the current node view
|
||
selected: {
|
||
type: Boolean,
|
||
},
|
||
|
||
// access to the node extension, for example to get options
|
||
extension: {
|
||
type: Object,
|
||
},
|
||
|
||
// get the document position of the current node
|
||
getPos: {
|
||
type: Function,
|
||
},
|
||
|
||
// update attributes of the current node
|
||
updateAttributes: {
|
||
type: Function,
|
||
},
|
||
|
||
// delete the current node
|
||
deleteNode: {
|
||
type: Function,
|
||
},
|
||
},
|
||
}
|
||
</script>
|
||
```
|
||
|
||
If you just want to have all (and TypeScript support) you can import all props:
|
||
|
||
```js
|
||
// Vue 3
|
||
import { defineComponent } from 'vue'
|
||
import { nodeViewProps } from '@tiptap/vue-3'
|
||
export default defineComponent({
|
||
props: nodeViewProps,
|
||
})
|
||
|
||
// Vue 2
|
||
import Vue from 'vue'
|
||
import { nodeViewProps } from '@tiptap/vue-2'
|
||
export default Vue.extend({
|
||
props: nodeViewProps,
|
||
})
|
||
```
|
||
|
||
## Dragging
|
||
To make your node views draggable, set `draggable: true` in the extension and add `data-drag-handle` to the DOM element that should function as the drag handle.
|
||
|
||
https://embed.tiptap.dev/preview/GuideNodeViews/DragHandle
|