VueJS: отслеживание порядка дочерних компонентов в родительском слоте во время выполнения

У меня есть пользовательский компонент, в котором родительский и массив дочерних компонентов тесно связаны, и между родительским и дочерним компонентами существует «черный ход» с использованием функции предоставления-инжекта, предоставляемой vue.

В родительском компоненте я хотел бы отслеживать дочерние компоненты в том порядке, в котором они определены в слоте.

Я знаю, что при подключении дочерние компоненты создаются в том порядке, в котором они определены. Однако во время выполнения дочерний компонент может быть вставлен, например, между первым дочерним компонентом и вторым дочерним компонентом. Вопрос в том, как узнать обновленный порядок дочерних компонентов в родительском слоте?

Я видел в vue devtools, что он может распознавать порядок дочерних и вложенных компонентов на вкладке «Компоненты» и отображать его в виде дерева. Как я могу добиться чего-то подобного?

Вот пример кода https://stackblitz.com/edit/vitejs-vite-pkqhkv?file=src%2FApp.vue

В приведенном выше примере, если вы нажмете «Предыдущий» и «Далее», он должен отобразить слайд 1 и слайд 3, как и ожидалось, поскольку слайд 2 отключен, но когда вы включаете слайд 2, порядок меняется на 1, 3, 2, что не то, что я хочу. Порядок должен быть 1, 2 и 3.

Очевидно, что реализация предоставления каждому дочернему элементу нумерованного индекса не является надежной, поскольку они могут быть зарегистрированы в разное время, поэтому я хочу реализовать по-другому, чтобы я мог знать текущий порядок дочерних компонентов в любой момент времени.

Пожалуйста, укажите stackoverflow.com/help/mcve для решения вашей проблемы. Это зависит от способа динамического изменения порядка. Если есть v-for, вы можете отслеживать массив и ключи, которые определяют, что он отображает.

Estus Flask 27.04.2024 10:00
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
80
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Слоты передаются как массив объектов, содержимое внутреннего элемента передается как значение внутри объекта. добавлены новые слоты с использованием сращивания массивов. Вы можете использовать v-html или другие методы, если дочерний компонент имеет больше элементов.

Родительский компонент

<template>
   <div>
     <slot></slot>
     <ul>
        <li v-for = "(s,i) in slotsArray" :key = "i">
            <slot :name = "s.name"></slot>
        </li>
     </ul>
 </div>
</template>
<script>
  export default {
    props: {
      slotsArray: { type: Array, default: null }
    }
  };</script>

Использование родительских компонентов и добавление слотов

<template>
  <div>
     <!-- Parent Component -->
     <parent-component :slotsArray = "slotsArray">
         <button @click = "addSlotToIndex(2, 'Item 21')">Add Item on 2nd 
            Index</button>
         <button @click = "addSlotToIndex(0, 'Item 00')">Add Item on 1st 
            Index</button>
         <template v-for = "(s) in slotsArray">
             <template :slot = "s.name">
                <p :key = "s.i">{{s.child}}</p>
             </template>
         </template>
  </parent-component></div></template>

<script>
import ParentComponent from './views/parent-child- 
    slots/ParentComponent.vue';

export default {
    name: 'App',
    components: { ParentComponent },
    data () {
        return {
            slotsArray: [
                {
                    name: 'slota',
                    child: 'Item 1'
                },
                {
                    name: 'slotb',
                    child: 'Item 2'
                },
                {
                    name: 'slotc',
                    child: 'Item 3'
                }
            ],
            addSlotToIndex (idx, value) {
                this.slotsArray.splice(idx, 0, {
                    name: idx > 0 ? this.slotsArray[idx - 1].name + 'a' : 
                     this.slotsArray[0].name + 'a',
                   child: value
            });
        }
    };
}};

спасибо за ответ, но я бы предпочел не передавать массив в качестве реквизита, потому что я хочу, чтобы потребитель мог передавать шаблон в дочернем компоненте. вы можете посмотреть первый пост, где я поделился ссылкой на пример кода.

icube 27.04.2024 14:46
Ответ принят как подходящий

Даже ваши родительские/дочерние компоненты сильно связаны, вам лучше не передавать состояние видимости слайда дочерним элементам. Это не очень хороший дизайн, слишком большая утечка состояния слайда. Сделайте всю логику слайдов внутри родителя (хороший дизайн и инкапсуляция).

ИГРОВАЯ ПЛОЩАДКА VUE SFC

Родитель.vue

<script setup>

import { ref, useSlots, watch, computed } from 'vue';

// the current component index
const current = ref(0);

const slots = useSlots();
// collect all slot child components
const $children = computed(() => slots.default().filter(vnode => vnode.type.render)); // remove all non-component vnodes
// watch for deletion of the current component and move to the next one
watch(() => $children.value.length, length => current.value < length || next(current.value - 1));


function prev(idx = current.value) {
  if (--idx < 0) idx = $children.value.length - 1;
  current.value = idx;
}

function next(idx = current.value) {
  if (++idx >= $children.value.length) idx = 0;
  current.value = idx;
}

defineExpose({
  prev,
  next,
});
</script>

<template>
  <section>
    <div>$children.length: {{ $children.length }}, current: {{ current }}</div>
    <component :is = "() => $children.filter((_, idx)=> idx === current)" />
  </section>
</template>

App.vue

<script setup>
import Parent from './Parent.vue';
import Child from './Child.vue';
import { ref } from 'vue';

const toggleSlide2 = ref(false);
const toggleSlide3 = ref(true);
const parent = ref();

</script>

<template>
  <div>
    <section>
      <button @click = "parent.prev()">Prev</button>
      <button @click = "parent.next()">Next</button>
      <button @click = "toggleSlide2 = !toggleSlide2">Toggle Slide 2</button>
       <button @click = "toggleSlide3 = !toggleSlide3">Toggle Slide 3</button>
    </section>

    <Parent ref = "parent">
      <Child>
        <h1>Slide 1</h1>
      </Child>
      <Child v-if = "toggleSlide2">
        <h1>Slide 2</h1>
      </Child>
      <Child v-if = "toggleSlide3">
        <h1>Slide 3</h1>
      </Child>
    </Parent>
  </div>
</template>

спасибо за ответ, но родитель и ребенок созданы для сильной связи

icube 28.04.2024 07:24

@icube да, имейте сильную связь, предоставляйте/внедряйте, без проблем. но, по моему мнению, утечка скользящего механизма в детей - это плохой дизайн. нет необходимости в этом

Alexander Nenashev 28.04.2024 10:26

@icube я обновил ответ, чтобы уточнить это

Alexander Nenashev 28.04.2024 10:56

спасибо за разъяснения, но причина моего вопроса в том, чтобы спросить, есть ли способ отслеживать порядок дочерних элементов от родителя, как указано в слоте, без необходимости явно устанавливать порядок дочерним элементом. если бы у меня был ребенок, который мог бы явно установить порядок, я бы вообще не задавал вопрос, поскольку знаю, что могу легко получить информацию

icube 28.04.2024 17:57

@icube, что не так с моим решением? у вас есть логика слайдов, которая работает так, как вас просят

Alexander Nenashev 28.04.2024 18:53

в вашем решении нет ничего плохого, и оно работает, да, но мой вопрос заключается в том, как отслеживать порядок дочерних элементов в родительском слоте в том порядке, в котором они были объявлены в слоте, без явного указания ключа заказа.

icube 30.04.2024 16:01

Продолжение Первого ответа. Попробуйте передать дочерний компонент таким образом. Недостатком является необходимость добавить html как строку и привязать как v-html.

<parent-component :slotsArray = "slotsArray">
  <button @click = "addSlotToIndex(2, 'Item 21')">Add Item on 2nd 
     Index</button>
  <button @click = "addSlotToIndex(0, 'Item 00')">Add Item on 1st 
     Index</button>
   <template v-for = "(s) in slotsArray">
    <template :slot = "s.name">
      <child-component :inner-html = "s.child" :key = "s.i"></child-component>
    </template>
  </template>
</parent-component>

Дочерний компонент

<template>
   <component :is = "tag">
     <slot>
       <div v-html = "innerHtml"></div>
     </slot>
   </component></template>



   <script>
  export default {
    props: {
       innerHtml: { type: String, default: '' },
       tag: { type: String, default: 'div' }
    }
  };</script>

Примечание. Может оказаться бесполезным, если дочерний компонент сложен и содержит больше элементов.

Другие вопросы по теме