Проблема с выводом 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 шт):

Автор решения: yar85

Директива 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">

→ Ссылка