Проблема с выводом html кода в дочернем компоненте
У меня есть выпадающий список(ExpansionPanel) из vuetify и я хочу сделать этот компонент динамическим, чтобы я мог передавать заголовок и текст каждого пункта, проблема заключается в том, что в тексте который я передаю может быть слово, которое должно быть ссылкой, поэтому передавать обычный текст не получится и я решил передавать объект, который содержит заголовок и html код. Если в дочернем компоненте выводить эту разметку, то она выводится прямо с тегами, а если использовать слоты с атрибутом v-html, то не будет работать ссылка. Стоит еще уточнить, что мне обязательно нужно использовать router-link, а не обычный тег Вот пример Массива который я передаю в expansion panel
items:[
{
label:'A',
text:'<p>Random Text</p>\
<p><router-link to=\'/\'>Перейти к списку заданий</router-link></p>'
},
{
label:'B',
text:'<p>Just a text</p>'
}
]
Вот так передаю этот массив в дочерний элемент
<areal-expansion-panel :items="items" />
И вот сам код дочернего элемента
<template>
<v-expansion-panels accordion flat>
<v-expansion-panel
v-for="(item,i) in items"
:key="i"
:class="i === 0 ? 'expansion-panel-first-item' : null "
>
<v-expansion-panel-header>
{{item.label}}
<slot name="label" :item="item"></slot>
</v-expansion-panel-header>
<v-expansion-panel-content>
{{item.text}}
<slot v-html="item.text"></slot>
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
</template>
Есть ли какой нибудь способ сделать это с помощью слотов, например определять шаблон прямо в родительском компоненте?
Ответы (1 шт):
Директива v-html предназначена для вывода HTML-разметки, что легко понять по ее имени - и, конечно же, ее значение не компилируется как template (это вполне ясно-однозначно написано в документации Vue).
Поэтому, компонент <router-link> и не обрабатывается... потому что не должен: это не элемент HTML. То же самое касается и любых других vue-компонентов, внутри v-html они ожидаемо не будут "работать".
Есть ли какой нибудь способ сделать это с помощью слотов
Да, есть:
Vue.component('areal-expansion-panel', {
template: `
<v-expansion-panels accordion>
<v-expansion-panel v-for="(item, i) in items" :key="i"
:class=" { 'expansion-panel-first-item': i === 0 }"
>
<v-expansion-panel-header>
<slot name="label" :item="item">{{ item.label }}</slot>
</v-expansion-panel-header>
<v-expansion-panel-content>
<slot name="text" :item="item">
<p v-html="item.text"></p>
<p v-if="item.link">
<router-link :to="item.link.to">{{ item.link.text }}</router-link>
</p>
</slot>
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
`,
props: ['items'],
});
new Vue({
el: '#app',
router: new VueRouter({}),
vuetify: new Vuetify(),
data() { return {
items: [
{
label: 'A',
text: 'Random markup <i>with link</i>',
link: { to: '/', text: 'Перейти к списку заданий' },
}, {
label: 'B',
text: 'Just a text',
},
],
}},
});
<div id="app">
<v-app>
<v-main>
<v-container>
<areal-expansion-panel :items="items"></areal-expansion-panel>
</v-container>
</v-main>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.js"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet">
Ну и, конечно же, если требуется кастомный вывод текста со ссылкой - слот дает такую возможность:
Vue.component('areal-expansion-panel', {
template: `
<v-expansion-panels accordion>
<v-expansion-panel v-for="(item, i) in items" :key="i"
:class=" { 'expansion-panel-first-item': i === 0 }"
>
<v-expansion-panel-header>
<slot name="label" :item="item">{{ item.label }}</slot>
</v-expansion-panel-header>
<v-expansion-panel-content>
<slot name="text" :item="item">
<p v-html="item.text"></p>
<p v-if="item.link">
<router-link :to="item.link.to">{{ item.link.text }}</router-link>
</p>
</slot>
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
`,
props: ['items'],
});
new Vue({
el: '#app',
router: new VueRouter({}),
vuetify: new Vuetify(),
data() { return {
items: [
{
label: 'A',
text: 'Random markup <i>with link</i>',
link: { to: '/', text: 'Перейти к списку заданий' },
}, {
label: 'B',
text: 'Just a text',
},
],
}},
});
a { text-decoration: none; }
<div id="app">
<v-app>
<v-main>
<v-container>
<areal-expansion-panel :items="items">
<template v-slot:text="{ item: { text, link }}">
<p class="d-flex justify-space-between align-center mb-0">
<span v-html="text"></span>
<router-link v-if="link" :to="`some-prefix${link.to}`" :title="link.text">
<v-btn icon color="blue" icon>
<v-icon>mdi-home</v-icon>
</v-btn>
</router-link>
</p>
</template>
</areal-expansion-panel>
</v-container>
</v-main>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.js"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet">