How to use nested nodes inside a Vue Component
See original GitHub issueWhich part of the documentation have you read? https://next.tiptap.dev/examples/interactive
Did you find the content helpful? Yes, but i think that what we are trying to develop is something that is yet to be covered, or we just didn’t have the full understanding of how things work
What was hard to understand, missing or misleading? We are trying to build a Quiz with Multiple Choices, we were able to get something out of it, but the way it works seems a bit off, we used inputs in the vue component to synchronise the attributes (eg: title, description) with the custom node, but we would like to use a heading and paragraph from the tiptap extensions, to use the getJSON() method to output our custom node with content and attrs inside of it. Is it possible to add two or more nodes of content inside a custom node within a vue component?
What else? Here is what we tried to do, hope it was in the right direction
Vue Component
<template>
<node-view-wrapper class="block-wrapper">
<div class="question-container" v-if="state.studentAnswer.index < 0">
<p class="green-subtitle">Exercício</p>
<div class="title">{{ state.title }}</div>
<div class="description">{{ state.description }}</div>
<div class="black-subtitle">Escolha abaixo</div>
<div
class="answer-wrapper"
:class="{
'answer-wrapper-correct':
index === state.studentAnswer.index && state.studentAnswer.isCorrect
}"
v-for="(answer, index) in state.answers"
:key="index"
@click="() => verifyAnswer(index)"
>
{{ answer.text }}
</div>
</div>
<div class="answer-container" v-if="state.studentAnswer.index >= 0">
<p class="green-subtitle">Resposta</p>
<div class="answer-title">{{ state.title }}</div>
<div
class="correct-answer-container"
v-if="state.studentAnswer.isCorrect"
>
<div class="black-subtitle">Acertou!</div>
<div class="correct-answer">
{{ state.answers[state.studentAnswer.index].text }}
</div>
</div>
<div class="incorrect-answer-container" v-else>
<div class="black-subtitle">Sua Resposta:</div>
<div class="incorrect-answer">
{{ state.answers[state.studentAnswer.index].text }}
</div>
<div class="black-subtitle">Resposta Certa:</div>
<div class="correct-answer">
{{ state.answers[correctAnswerIndex].text }}
</div>
</div>
<div class="answer-complement-text">
<p>{{ state.answerComplementText }}</p>
</div>
<div class="page-break-wrapper">
<hr />
<button @click="() => {}">
Avançar
</button>
</div>
</div>
</node-view-wrapper>
</template>
<script lang="ts">
import { computed, defineComponent, reactive } from 'vue'
import { NodeViewWrapper } from '@tiptap/vue-3'
export default defineComponent({
name: 'MultipleChoice',
components: {
NodeViewWrapper
},
setup() {
const state = reactive({
title:
'',
description:
'',
answers: [
{
text:
'',
isCorrect: false
},
{
text:
'',
isCorrect: true
},
{
text:
'',
isCorrect: false
}
],
studentAnswer: {
index: -1,
isCorrect: false
},
answerComplementText:
''
})
const correctAnswerIndex = computed(() =>
state.answers.findIndex(answer => answer.isCorrect)
)
const verifyAnswer = (index: number) => {
state.studentAnswer.index = index
state.studentAnswer.isCorrect = state.answers[index].isCorrect
}
return { state, verifyAnswer, correctAnswerIndex }
}
})
Typescript Node Implementation
import { Command, mergeAttributes, Node } from '@tiptap/core'
import { VueNodeViewRenderer } from '@tiptap/vue-3'
import Component from './index.vue'
declare module '@tiptap/core' {
interface Commands {
multipleChoice: {
addMultipleChoiceBlock: () => Command
}
}
}
export default Node.create({
name: 'multipleChoice',
group: 'block',
atom: true,
draggable: true,
addCommands() {
return {
addMultipleChoiceBlock: () => ({ commands }) => {
return commands.insertHTML('<multiple-choice></multiple-choice>')
}
}
},
parseHTML() {
return [{ tag: 'multiple-choice' }]
},
renderHTML({ HTMLAttributes }) {
return ['multiple-choice', mergeAttributes(HTMLAttributes)]
},
addNodeView() {
return VueNodeViewRenderer(Component)
}
})
Issue Analytics
- State:
- Created 2 years ago
- Reactions:2
- Comments:5 (2 by maintainers)
I have a similar use case, where I want to insert a specifically formatted title in combination with a paragraph. Is there a possibility to do this via a Component with multiple
NodeViewContent
s?The content of a node is always controlled by the content setting. A node view has nothing to do with that and renders always a single node.