Вопрос или проблема
У меня есть компонент (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
, который позволит родительскому компоненту обращаться к функциям дочернего компонента, с которым он работает через слот.
Шаги по реализации:
- Экспортируйте нужный метод в компоненте 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>
- В компоненте 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>
- В родительском компоненте 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
через этот реф.
Этот подход обеспечивает эффективный способ взаимодействия между компонентами, учитывая динамическую структуру вложенных компонентов.