Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue.js ref inside the v-for loop

I tried to use components inside v-for loop and init the ref to future access some methods of these from parent. Here a simplified code of my case:

<template>     <div class="hello">         {{ msg }}         <ul>             <list-item                  v-for="item in items"                  :key="item.id"                  :value="item.text"                  :ref="`item${item.id}`"             />         </ul>     </div> </template>  <script>     import ListItem from "./ListItem";     export default {         name: "HelloWorld",         components: {             ListItem         },         data() {             return {                 msg: "Welcome to Your Vue.js App",                 items: [                     { id: 1, text: "foo" },                     { id: 2, text: "bar" },                     { id: 3, text: "baz" },                     { id: 4, text: "foobar" }                 ]             };         },         mounted() {             setTimeout(() => this.$refs.item2.highlight(), 1500);         }     }; </script> 

And ListItem component:

<template>     <li v-bind:class="{ highlight: isHighlighted }">         {{value}}     </li> </template>  <script>     export default {         name: "list-item",         props: ["value"],         data() {             return {                 isHighlighted: false             };         },         methods: {             highlight() {                 this.isHighlighted = !this.isHighlighted;             }         }     }; </script>  <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped>     .highlight {         color: red;     } </style> 

It's just renders a few list items and highlights one of them after one and half second. But I got an error: Uncaught TypeError: _this.$refs.item2.highlight is not a function
After debug session I've found an interesting fact: refs defined inside v-for loop are not a components but the arrays with one component.
What is the logic, what is the f wrapper? Does anyone meet this case? Can somebody give the explanation of this behaviour?
Code presented above works fine with setTimeout(() => this.$refs.item2[0].highlight(), 1500);
Must I always pass [0]? Is there exist a better way? Help, please.

like image 938
Lev Khruschev Avatar asked Aug 29 '18 21:08

Lev Khruschev


People also ask

How do you access the ref at Vue?

Accessing the Refs Note that you can only access the ref after the component is mounted. If you try to access $refs.input in a template expression, it will be null on the first render. This is because the element doesn't exist until after the first render!

How is this refs option used in Vue?

Ref s are Vue. js instance properties that are used to register or indicate a reference to HTML elements or child elements in the template of your application. If a ref attribute is added to an HTML element in your Vue template, you'll then be able to reference that element or even a child element in your Vue instance.

What is the difference between ref and reactive Vue?

Unlike ref , reactive can only be initialized with an object. Each property of the object can be a different reactive variable, however. One advantage of reactive is that it doesn't use a value property so it may be a little easier to read. It also means it looks the same in JavaScript as in the template.

What does V bind do in Vue?

The v-bind directive is a Vuejs directive used to bind one or more attributes, or a component prop to an element. If that attribute is binded to our data defined in Vuejs instance then dynamically changes can be observed as data changes.


2 Answers

When using refs with v-for, the component / DOM nodes are stored as an array directly to the variable name so you don't need to use index number in the ref name. So you can do this:

<list-item   v-for="item in items"    :key="item.id"    :value="item.text"    ref="items" /> 

And use the refs in your component like this:

this.$refs.items[index] 

Also note that the refs may not be in order and would need to be handled in a different way which is a completely different issue. You can follow that here: https://github.com/vuejs/vue/issues/4952

like image 56
Sagar Chakravarthy Avatar answered Nov 01 '22 01:11

Sagar Chakravarthy


For Vue 3 users:

In Vue 3, such usage will no longer automatically create an array in $refs. To retrieve multiple refs from a single binding, bind ref to a function which provides more flexibility (this is a new feature):

HTML

<div v-for="item in list" :ref="setItemRef"></div> 

With Options API:

export default {   data() {     return {       itemRefs: []     }   },   methods: {     setItemRef(el) {       if (el) {         this.itemRefs.push(el)       }     }   },   beforeUpdate() {     this.itemRefs = []   },   updated() {     console.log(this.itemRefs)   } } 

With Composition API:

import { onBeforeUpdate, onUpdated } from 'vue'  export default {   setup() {     let itemRefs = []     const setItemRef = el => {       if (el) {         itemRefs.push(el)       }     }     onBeforeUpdate(() => {       itemRefs = []     })     onUpdated(() => {       console.log(itemRefs)     })     return {       setItemRef     }   } } 

Here is the doc link: https://v3.vuejs.org/guide/migration/array-refs.html

like image 39
Syed Avatar answered Nov 01 '22 03:11

Syed