TomWTG
TomWTG

Reputation: 31

Vue 3 & Composition API : template refs in v-for loop error : only get proxies

I've been developing my first project with Vue3.js & Vue Cli and I've been stuck for the past couple of hours on this bit of code.

Basically what I'm trying to do is to have a list of buttons based on an array of objects created in the setup() part of the code. All objects also contain their own ref within the array itself, which I eventually bind on the template. I then make consts out of each ref so that I can use them within the setup() but when I console.log(btnConvert.value) I get a proxy, which I don't with my other refs that aren't within a v-for loop.

    RefImpl {_rawValue: Proxy, _shallow: false, __v_isRef: true, _value: Proxy}

Here's the expanded version of the console.log(btnConvert.value)

Proxy {…}
  [[Handler]]: Object
    get: ƒ get({ _: instance }, key)
    has: ƒ has({ _: { data, setupState, accessCache, ctx, appContext, propsOptions } }, key)
    ownKeys: (target) => {…}
    set: ƒ set({ _: instance }, key, value)
  [[Prototype]]: Object
  [[Target]]: Object
  [[IsRevoked]]: false

I tried everything I could think of, but I couldn't understand the official Vue doc. Could anyone help me understand how I could retrieve the DOM elements with those refs? Thank you very much !

Here's the bit of relevant code (I removed the functions to which the btn refer for ease of lecture).I can add some more if necessary.

    <template>
      <div>
        <div ref="btnList" class="options">
          <vBtn
            v-for="btn in btnArray"
            :key="btn"
            :ref="btn.ref"
            class="btn"
            :class="`btn--${btn.class}`"
            @click="btn.action"
            v-html="btn.text"
          />
      </div>
    </template>
    <script>
    import { ref, onMounted } from 'vue'
    import vBtn from '@/components/Tool/vBtn.vue'

    export default {
      components : {
        vBtn
      },

      setup() {
        const btnConvert = ref(null)
        const btnCopy = ref(null)
        const btnCancel = ref(null)
        const btnUndo = ref(null)
        const btnErase = ref(null)
        
        const btnArray = [
          {
            class: 'convert',
            text: 'some text',
            action: convertText,
            ref: btnConvert
          },
          {
            class: 'undo',
            text: 'some text',
            action: undoConvert,
            ref: btnUndo
    
          },
          {
            class: 'cancel',
            text: 'some text',
            action: cancelChange,
            ref: btnCancel
    
          },
          {
            class: 'erase',
            text: 'some text',
            action: eraseText,
            ref: btnErase
    
          },
          {
            class: 'copy',
            text: 'some text',
            action: copyText,
            ref: btnCopy
          }
        ]
    
        onMounted() {
          console.log(btnConvert.value)
          // this is where I get the proxy
        }
      },
    }
    </script>

Upvotes: 3

Views: 9079

Answers (1)

Michal Lev&#253;
Michal Lev&#253;

Reputation: 37883

I am sorry but I am not able to replicate your results

  1. I do not understand how you can get anything else then null from console.log(btnConvert.value) when you are not rendering 1st button at all thanks to v-for="btn in btnArray.slice(1)" (which effectively creates new array without the first element in source array)

  2. It just works! See below example

Just a note:

Could anyone help me understand how I could retrieve the DOM elements with those refs?

Because ref is placed on Vue component (vBtn), it will never be an HTML element. It will be always a component instance...

const app = Vue.createApp({
  setup() {
    const buttons = ['convert', 'undo', 'cancel', 'erase', 'copy']
    const action = (param) => alert(param)
    const btnArray = buttons.map((item) => ({
        text: item,
        action: action,
        ref: Vue.ref(null)
    }))
    
    Vue.onMounted(() => {
      console.log(btnArray[0].ref.value)
      console.log(btnArray)
    })
    
    return {
      btnArray
    } 
  }
})

app.mount("#app")
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.11/vue.global.js" integrity="sha512-1gHWIGJfX0pBsPJHfyoAV4NiZ0wjjE1regXVSwglTejjna0/x/XG8tg+i3ZAsDtuci24LLxW8azhp1+VYE5daw==" crossorigin="anonymous"></script>
<div id="app">
  <button 
    v-for="(but, i) in btnArray" 
    :key="i" 
    @click="but.action(but.text)"
    :ref="but.ref"
   >  
    {{ but.text }}
   </button>
</div>

Upvotes: 5

Related Questions