Как вызвать функцию в слотовом компоненте из родительского?

Вопрос или проблема

У меня есть компонент (Child), который имеет слот. В нем также есть кнопка, которую я хочу использовать для вызова функции в компоненте внутри слота (GrandChild). Это возможно? И как это реализовать? Я пытался использовать ref на слоте, но без успеха. Также рассматривалScoped slot. Я использую vue3 с TypeScript.

Child не знает, какой компонент должен находиться в слоте, но, возможно, это можно передать как prop.

<!-- Parent.vue -->

<script setup lang="ts">
    import Child from './TestSlotsChild.vue';
    import GrandChild from './TestSlotsGrandChild.vue';
</script>

<template>
    <div class="parent">
        <h2>Я родитель</h2>
        <Child>
            <GrandChild/>
        </Child>
    </div>
</template>
<!-- Child.vue -->

<script setup lang="ts">
</script>

<template>
    <div class="child">
        <h3>Я ребенок</h3>
        <slot>
        </slot>
        <button>Вызвать myMethod в GrandChild</button>
    </div>
</template>
<!-- GrandChild.vue -->

<script setup lang="ts">
    import { ref } from 'vue';

    const called = ref(false);

    function myMethod() {
        called.value = true;
        console.log('myMethod вызван в компоненте grandchild');
    }
</script>

<template>
    <div class="grandchild">
        <h4>Я внук</h4>
        <p v-if="called">Родитель вызвал <em>myMethod</em> внука.</p>
    </div>
</template>

Экспонируйте метод из компонента GrandChild, чтобы получить к нему доступ с использованием ref :

<script setup lang="ts">
import { ref } from 'vue';

const called = ref(false);

function myMethod() {
  called.value = true;
  console.log('myMethod вызван в компоненте grandchild');
}

defineExpose({
  myMethod //<--- Экспонируйте его таким образом
})
</script>

<template>
  <div class="grandchild">
    <h4>Я внук</h4>
    <p v-if="called">Родитель вызвал <em>myMethod</em> внука.</p>
  </div>
</template>

Затем вы можете эмитировать событие из компонента Child :

<script setup lang="ts">

const emit = defineEmits(['click:btn'])

</script>

<template>
    <div class="child">
        <h3>Я ребенок</h3>
        <slot>
        </slot>
        <button @click="emit('click:btn')">Вызвать myMethod в GrandChild</button>
    </div>
</template>

Наконец, добавьте ref на компонент GrandChild и вызовите его экспонированный метод, используя эмитированное событие из компонента Child :

<script setup>
import { useTemplateRef } from 'vue';
import Child from './Child.vue';
import GrandChild from './GrandChild.vue';

const gc = useTemplateRef('grandchild')

</script>

<template>
  <div class="parent">
    <h2>Я родитель</h2>
    <Child @click:btn="gc.myMethod()">
      <GrandChild ref="grandchild" />
    </Child>
  </div>
</template>

Ответ или решение

Вопрос: Как вызвать функцию в дочернем компоненте, находящемся в слоте родительского компонента?

Для решения данной задачи необходимо использовать механизм ref и метод defineExpose, который позволит родительскому компоненту обращаться к функциям дочернего компонента, с которым он работает через слот.

Шаги по реализации:

  1. Экспортируйте нужный метод в компоненте GrandChild с помощью defineExpose, чтобы сделать его доступным для родительского компонента.
<!-- GrandChild.vue -->
<script setup lang="ts">
import { ref } from 'vue';

const called = ref(false);

function myMethod() {
  called.value = true;
  console.log('myMethod called in grandchild component');
}

// Экспортируем myMethod с помощью defineExpose
defineExpose({
  myMethod
});
</script>

<template>
  <div class="grandchild">
    <h4>Я - внук</h4>
    <p v-if="called">Родитель вызвал <em>myMethod</em> в компоненте внука.</p>
  </div>
</template>
  1. В компоненте Child создайте кнопку, которая будет эмитировать событие. Это событие будет использоваться для вызова метода в компоненте GrandChild.
<!-- Child.vue -->
<script setup lang="ts">
const emit = defineEmits(['click:btn']);
</script>

<template>
  <div class="child">
    <h3>Я - ребенок</h3>
    <slot></slot>
    <button @click="emit('click:btn')">Вызвать myMethod в GrandChild</button>
  </div>
</template>
  1. В родительском компоненте Parent получайте ссылку на компонент GrandChild и вызывайте его метод при нажатии на кнопку в Child.
<!-- Parent.vue -->
<script setup lang="ts">
import { ref } from 'vue';
import Child from './Child.vue';
import GrandChild from './GrandChild.vue';

const gc = ref(null); // создаем реф для GrandChild
</script>

<template>
  <div class="parent">
    <h2>Я - родитель</h2>
    <Child @click:btn="gc.myMethod()">
      <!-- Добавляем ref к компоненту GrandChild -->
      <GrandChild ref="gc" />
    </Child>
  </div>
</template>

Объяснение:

  • В GrandChild.vue мы определили метод myMethod и экспортировали его с помощью defineExpose, что позволяет другим компонентам вызывать этот метод через ссылку.
  • В Child.vue кнопка вызывает событие при нажатии, которое передается родителю через $emit.
  • В Parent.vue мы используем ref для получения доступа к экземпляру компонента GrandChild. Когда кнопка нажата, мы вызываем myMethod через этот реф.

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

Оцените материал
Добавить комментарий

Капча загружается...