I'm using Vue 3 and in a v-for loop, I'm creating multiple button elements. The buttons are being made in another component named wb-button. So I call wb-button in every v-for loop.
I add @click event listener to the wb-button that calls a method inside the current component, simple:
<div v-for="(item,key) in items" :key="key">
<span>{{item.name}}</span>
<wb-button @click="deleteItem(item)">
Delete item!
</wb-button>
</div>
This works how I want, the problem starts when I want to pass the wb-button just like a ref to the deleteItem method. The purpose is to make changes to another ref inside the wb-button. So what I basically want to do is this:
export default{
name: 'listComponent',
methods:{
async deleteItem(item,wbButtonRef){
// The next line is what I want to do
wbButtonRef.$refs.btnElement.putInLoadingStatus()
// do the delete item action
}
}
}
I know I can create a ref on each wb-button and pass an ID or something to the method, but I don't think it is the cleanest way to do it.
If there was something to just pass the wb-button element as the method parameter it would be great. Something like this:
<!-- I want to know if there is something I can put in
the second argument of the 'wb-button' @click event -->
<wb-button @click="deleteItem(item, /* what to put here?*/)">
<!-- "this","$this","$event.target" etc ... -->
I have tried $event.target but it returns the root element of wb-button, what I need is the wb-button itself just like a $ref.
Simply put, you can't. And since this logic is relevant only for the button component itself, it's best to keep this logic within it. Adding a prop and render something based on that, like you suggested yourself in the comments, is a good way to go about it.
Considering Other Options
Although I used @paddotk 's answer, 'the props way' to solve my problem, I'm just adding this answer so anyone who reads this question afterward would have a complete answer.
As far as I have found out, there are two more ways of doing this:
1- As @MrFabio_25 mentioned in the comments, I can create a custom event on the child component and $emit with 'this' as a parameter, so I can handle that in the parent:
// wbButton.vue file
<inside-wb-component ref="btnElement" @click="handleClick">
<button>
<slot></slot>
</button>
</inside-wb-component>
//and in the script tag
//...
methods:{
handleClick(){
this.$emit('buttonClick',this)
}
}
//...
And simply in the parent component:
<wb-button @buttonClick="handleButtonClick">
A text here
</wb-button>
// in the script tag
methods:{
handleButtonClick(elem){
elem.$refs.btnElement.putInLoadingStatus()
}
}
2- The second way to do so, without the child component being involved, is to use an array of refs, as explained here: Vue 3 Documentation - refs inside v-for
<div v-for="(item,key) in items" :key="key">
<wb-button ref="wbButtonRefs" @click="handleButtonClick(item,key)">
A text here
</wb-button>
</div>
// and in scripts tag
//...
methods:{
handleButtonClick(item,index){
const buttonRef=this.$refs.wbButtonRefs[index]
// Now do whatever with buttonRef
buttonRef.$refs.btnElement.putInLoadingStatus()
}
}
//...