Is there a way to access and invoke a exposed function of a Vue component within a default slot?

Exploring the realms of a vue playground.

The functions interfaceFunction in both ChildA and ChildB are exposed.

In App, these functions can be called by obtaining references to the components that expose them. This allows direct function calls from within App.

For Container to access and call these functions from its children in the default slot, how can it obtain references to the components and invoke the functions?

Code snippet from App.vue:

<script setup>
import { ref} from 'vue'

import Container from './Container.vue'
import ChildA from './ChildA.vue'
import ChildB from './ChildB.vue'

const childa = ref(null)

function callInterfaceFunction() {
    childa.value.interfaceFunction()
}
</script>

<template>
    <button @click="callInterfaceFunction">Call from App</button>

    <Container>
        <ChildA ref="childa"/>
        <ChildB />
    </Container>
</template>

Code snippet from Container.vue:

<script setup>
import { useSlots} from 'vue'

const slots = useSlots()

function callInterfaceFunction() {
  const children = slots.default()

  for ( const child of children ) {
    child.interfaceFunction()
  }
}
</script>

<template>
  <button @click="callInterfaceFunction">Call from Container</button>

  <slot />
</template>

Code snippet from ChildA.vue:

<script setup>

function interfaceFunction() {
  console.log( "Called interfaceFunction A" )
}

defineExpose({ interfaceFunction })
</script>

<template>
  <div>
    ChildA
  </div>
</template>

Code snippet from ChildB.vue:

<script setup>

function interfaceFunction() {
  console.log( "Called interfaceFunction A" )
}

defineExpose({ interfaceFunction })
</script>

<template>
  <div>
    ChildB
  </div>
</template>

Answer №1

To update your slot's vnodes, simply add refs to them:

Please note: Consider switching from using h to cloneVNode, as the use of h is not officially documented yet: https://github.com/vuejs/docs/issues/2834

VUE SFC PLAYGROUND

<script setup>
import { useSlots, ref, h} from 'vue'

const slots = useSlots();
let $children;
const slotted = () => ($children = [], slots.default().map((vnode, i) => h(vnode, {ref: $children[i] ??= ref()})));

function callInterfaceFunction(){
  $children.forEach(({value: child}) => child.interfaceFunction());
}
</script>

<template>
  <button @click="callInterfaceFunction">Call from Container</button>
  <slotted/>
</template>

A more generalized approach involves recursively navigating nodes to gather refs (although this may not cover existing refs or slots, you can enhance it). This method allows for wrapping slotted components, like rendering them conditionally:

VUE SFC PLAYGROUND

<script setup>
import { useSlots, ref, h} from 'vue'

const slots = useSlots();
let $children;
const traverse = vnode => {
  vnode = h(vnode, {ref: $children[$children.length] ??= ref()});
  vnode.children = vnode.children?.map(traverse);
  return vnode;
};
const slotted = () => ($children = [], slots.default().map(traverse));

function callInterfaceFunction(){
  $children.forEach(({value:child}) => child.interfaceFunction?.());
}
</script>

<template>
  <button @click="callInterfaceFunction">Call from Container</button>
  <slotted/>
</template>
<script setup>
import { ref } from 'vue'

import Container from './Container.vue'
import ChildA from './ChildA.vue'
import ChildB from './ChildB.vue'

const childa = ref(null)

const swapped = ref(false);

function callInterfaceFunction() {
    childa.value?.interfaceFunction()
}
</script>

<template>
    <button @click="callInterfaceFunction">Call from App</button>

    <Container>
        <template v-if="!swapped"><ChildA ref="childa"/><ChildB/></template>
        <template v-else="swapped"><ChildB/><ChildA ref="childa"/></template>
    </Container>
    <button @click="swapped = !swapped">Swap</button>
</template>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring Node.js with a straightforward illustration and tackling the EPERM error

const server = require('http').createServer(function(req, res){ }); server.listen(8080); This code snippet is causing an error to be displayed in the console: $ node node_server.js node.js:201 throw e; // process.nextTick error, or &apos ...

Prevent Duplicate Service Instances in Angular

After doing some thorough research online, I've identified the root of my issue: multiple instances of a particular service are being created. I need assistance in pinpointing and rectifying this problem within my code. The secondary service is depen ...

Tips on combining multiple HTML tags within a single div element

After extracting a value from an XML document using Javascript, I encountered a problem while attempting to generate multiple image tags based on that value. Despite my attempt with a for loop, only one image was being displayed. for(i=0; i<red; i++){ ...

Error encountered during Jest snapshot testing: Attempting to destructure a non-iterable object which is invalid

I am currently facing an issue with my React codebase where I am attempting to create snapshot tests for a component. However, Jest is showing an error indicating that I am trying to destructure a non-iterable instance. Despite thoroughly reviewing the cod ...

What is the best way to add a border around an image along with a button using VueJS?

I am struggling to link a button and an image in VueJS to display a border around the picture. While I can successfully display the border on the button, I am unsure how to extend it to the image as well. Vue.component('my-button', 'my-img& ...

What is the best way to retrieve a variable in AngularJS1 after the HTML has been divided into multiple child HTML files?

I have segmented my main HTML page into multiple subpages and included them in the main file. However, it seems that each subpage is referencing different '$scope' variables. I am trying to reference ng-modle="My-model" from one subpage to anothe ...

The function URL.createObjectURL() is not recognized in Nuxt

Hey everyone, currently I'm utilizing Nuxt I have an image saved on the server as a blob that I want to display on the client side This is how my Component is structured: <template> <div class="product-page"> <div clas ...

When the onclick function is triggered, two or more characters will be displayed

Functionality: To input name and email, users must click on the virtual keyboard displayed on the screen. Characters entered will be shown in the input box. Implementation: The HTML keyboard interface and associated script have been developed successful ...

How to automatically select the first item in a populated dropdown list using Vue JS

My HTML select element is populated with options from a server, but when using v-model, it initially selects an empty option instead of the first one. I came across a solution on a post which suggests selecting the first option manually, but since the dat ...

How to ensure NodeJS waits for a response before returning a value

I've come across a seemingly simple problem that I just can't seem to solve. My current project involves working with an asynchronous messaging bot. In this particular scenario, the bot is supposed to react to an event by calling a Restful API a ...

Retrieve the content of a specific HTML tag using JavaScript

Is it possible to extract the content of a specific HTML tag as a string or XML format? <body> <script src="jquery.min.js"> //var test = document.getElementsByTagName("input"); //alert(test[0]); $(document).ready(function ( ...

Is there a way to develop a login form that retrieves information from a Google Sheet database?

Please help me with a solution. I have developed a registration form which effectively saves users' information in a Google Sheet. However, now I am yearning to create a login form that verifies the stored data and redirects the user to a different pa ...

Enhance the <div> with interactive content through dynamic updates on click within the same element

I am currently developing an Editor-Menu for a website administration. When I open admin.php, the Editor-Menu should be loaded into the #ausgabe div using the code $('#ausgabe').load('./admin-ajax/ajax2.php'). This functionality is work ...

An error was encountered: The object 'object object' does not have the 'on' method available

I want to experiment with this website: However, I am encountering an error without any events triggering. I am confused why the method "on" is not being found even on page load. <head> <link href="css/scrollable-horizontal.css" rel="styleshe ...

What are the steps for adding node packages to sublime text?

Is there a way to install node packages directly from Sublime Text instead of using the command line? If so, what is the process for doing this? I'm not referring to Package Control; I'm specifically interested in installing npm packages like th ...

Guide on positioning components to the right side of the NavigationDrawer in Vuetify with VueRouter

Working on my VueJS project, I've implemented a system to display content based on the user login using Firebase and Vuex state management. When a user is logged in, the content is shown using the v-if directive. Currently, I have successfully placed ...

What are some ways I can make my content come alive on my website using the Tympanus examples

After spending hours searching for a way to add animation to my content, I finally found a technique that seemed promising. Despite following the same steps as outlined in the tutorial I found, I was disappointed to discover that there was no animation o ...

Handlers for adjustments not correctly updating values in introduced object

I am facing an issue with a table that displays data fetched from an API and a select dropdown. Whenever a checkbox is selected, the name key along with its value is added to an array object named selectedFields. checkbox = ({ name, isChecked }) => { ...

Set the error state of a TextField in Material UI to true based on the user's input

Being a newcomer to React and Javascript, I have made some progress but now find myself stuck. I am struggling with how to change the error state of a Material UI TextField based on user input. Specifically, I want the error to be triggered if the user inp ...

Learn the steps to assign a Base64 URL to an image source

I am currently facing an issue with an image that is being used with angular-cli: <img src="" style="width: 120px; padding-top: 10px" alt="" id="dishPhoto"> The image has a Base64 url named imgUrl. My intention is to set the image source using the ...