How get ref from slot in vue 3?
Asked Answered
I

4

6

i need to to focus ref with name test1 a set some value which is placed in compontend slot (from outside). Is it possible to do it somehow? I tried to get from $refs or $slots, but failed.

App.vue

    <template>
      <div id="app">
        <HelloWorld>
          <input type="text" ref="test1" />
        </HelloWorld>
      </div>
    </template>
    
    ```
    <script>
    import HelloWorld from './components/HelloWorld.vue';
    
    export default {
      name: 'App',
      components: {
        HelloWorld,
      },
    };
    </script>
    
    <style>
    #app {
      font-family: Avenir, Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
      margin-top: 60px;
    }
    </style>

Component.vue

    <template>
      <slot></slot>
      <hr />
      <input type="text" ref="test2" />
    </template>
    
    <script>
    export default {
      name: 'HelloWorld',
      mounted() {
        this.$refs.test2.value = 'test 2 value';
        // how get ref 'test1' ?
      },
    };
    </script>
Inseverable answered 10/3, 2022 at 14:39 Comment(1)
It seems slightly counterintuitive, but I think this question and my recent question on vNode.componentInstance are related and once we have an answer for one, we'll have an answer for both. In both cases, we are trying to get details of a component passed in through a slot. #72399653Chronicle
A
4

You can simply pass a ref through an event or a prop. Here's a solution using your example.

   <!-- App.vue -->
   <div id="app">
    <HelloWorld :test="inputEl">
      <template v-slot:input>
        <input type="text" ref="test1" />
      </template>
    </HelloWorld>
   </div>

    <script>
        export default {
          name: 'App',
          mounted() {
            this.inputEl = this.$refs.test1;
          },
          data() {
            return {
              inputEl: {}
            }
          }
        };
    </script>
   
   <!-- HelloWorld.vue -->
   <template>
     <slot></slot>
     <hr />
     <input type="text" ref="test2" />
   </template>
   
   <script>
    export default {
      name: 'HelloWorld',
      mounted() {
        this.$refs.test2.value = 'test 2 value';
        
        // Use the prop. Or watch it until it has been updated.
        this.props.test.value = "test 1 value";
      },
    };
   </script>
Anthropography answered 1/6, 2022 at 22:27 Comment(3)
Hi, thanks for your comment, but doesn´t work. Live demo: stackblitz.com/edit/… Can you use $refs variable directly in template? It seems not, however I get undefined in the child componentInseverable
You're right. The $refs does not seem to work all the time when used in the template. You can still pass the component directly in your code. Here's a working version of your demo: stackblitz.com/edit/vue-frxfyq?file=src/App.vueAnthropography
Hi Prince Owen, thx, works great. I dont know if it is a preferrable approach, i discover maybe better -> https://mcmap.net/q/1700989/-how-get-ref-from-slot-in-vue-3 But i saved your approchach too, thx!Inseverable
L
4

In Vue3, you can use function refs and scoped slot.

example

// Parent
const slotRef = ref()
const setSlotRef = (el) => {
  slotRef.value = el;
}

<template>
  <slot name="child" :set-ref="setSlotRef">
</template>

// Child
<template #child="{ setRef }">
   <button :ref="(el) => setRef(el)">
     button
   </button>
</template>

Lamp answered 29/6, 2022 at 4:52 Comment(4)
Can you prepare demo pls? I cannot reproduce code that worksInseverable
Can you try this example? link - sfc.vuejs.org/…Lamp
Hi, thanks for the demo. I rewrote it in the Options API. It looks similar to my solution (https://mcmap.net/q/1700989/-how-get-ref-from-slot-in-vue-3), except that I set it from the top (parent to child), you from the bottom (from child to parent via scoped slot) I don't know which is better, but it's good to know about both. And the Composition API is not needed, i think. Thanks a lot.Inseverable
It's not a good solution, you can't write :ref="(el) => setRef(el)" for everything where you are using this slot, and it can be hundreds of components.Jackquelinejackrabbit
I
0

I have it!

<template>
  <div id="app">
    <HelloWorld name="child" :b="setRefOnA">
      <input type="text" ref="a" value="default value of this input" />
    </HelloWorld>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue';

export default {
  name: 'App',
  components: {
    HelloWorld,
  },
  methods: {
    setRefOnA(el) {
      return this.$refs.a;
    },
  },
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>


<!-- HelloWorld.vue -->
<template>
  <div>
    <slot> ... </slot>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: ['b'],
  mounted() {
    console.log(this.b().focus());
  },
};
</script>

Link: https://stackblitz.com/edit/vue-jynies?file=src/App.vue

Inseverable answered 22/7, 2022 at 13:41 Comment(0)
T
0

You define <input type="text" ref="test1" /> in App.vue so you need to execute this.$refs.test1.focus() in the App.vue too. You can listen and use parent component's mounted event using vue:mounted.

App.js

<template>
  <div id="app">
    <HelloWorld @vue:mounted="wrapperMounted">
      <input type="text" ref="test1" />
    </HelloWorld>
  </div>
</template>


<script>
import HelloWorld from './components/HelloWorld.vue';

export default {
  name: 'App',
  components: {
    HelloWorld,
  },
  methods: {
    wrapperMounted() {
      this.$nextTick(() => this.$refs.test1.focus());
    },
  },
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Component.vue

<template>
  <slot></slot>
  <hr />
  <input type="text" ref="test2" />
</template>

<script>
export default {
  name: 'HelloWorld',
  mounted() {
    this.$refs.test2.value = 'test 2 value';
    // how get ref 'test1' ?
  },
};
</script>
Tyrannize answered 28/5, 2023 at 11:14 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.