Reputation: 1500
I am trying to create a dropdown component, in which it receives various data and the list items are built dynamically, but I am having difficulty detecting the click on any item in the list
parent
<Dropdown
:items="[
{
text: 'Edit',
icon: 'fal fa-edit',
click: editFunction(id)
},
{
text: 'Delete',
icon: 'fal fa-trash-alt',
click: deleteFunction(id)
}
]"
/>
child Dropdown.vue
<template>
<div class="dropdown">
<a class="trigger">
<i class="far fa-ellipsis-h"></i>
</a>
<ul class="items">
<li v-for="(item, index) in items" :key="index" class="item">
<a>
<i :class="item.icon"></i>
{{ item.text }}
</a>
</li>
</ul>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
props: {
items: {
type: Array,
default: () => []
}
}
})
</script>
currently in this way, as soon as the parent component is created, the editFunction(id)
and deleteFunction(id)
methods are executed.
I know it's possible because vuetifyjs it does that way, but I tried to inspect the source code but I got nowhere.
Upvotes: 0
Views: 4158
Reputation: 2856
What you want to achieve can be done by
parent.vue
<child-componet :parent-method="thisIsTheMethod" />
...
methods: {
thisIsTheMethod()
{
//here it does something
}
}
note that the method passed inside the prop does not have the parenthesis ()
because you are passing a reference to it.
To use it inside the child component add the ()
@click="parentMethod()"
To go back to your example, change this:
<Dropdown
:items="[
{
text: 'Edit',
icon: 'fal fa-edit',
click: editFunction(id)
},
{
text: 'Delete',
icon: 'fal fa-trash-alt',
click: deleteFunction(id)
}
]"
/>
to
<Dropdown
:items="[
{
text: 'Edit',
icon: 'fal fa-edit',
click: () => editFunction(10)
},
{
text: 'Delete',
icon: 'fal fa-trash-alt',
click: () => deleteFunction(20)
}
]"
/>
and leave your editFunction(id)
method declaration as is. The argument will be automatically injected.
Despite this solution would work, the best way to achieve this communication between parent and child would be to emit the value in the child and then listen for it
Upvotes: 2
Reputation: 2761
there have betters ways to do it, but about your idea should be as below:
Vue.component('dropdown', {
template: '#dropdown-template',
props: {
items: {
type: Array,
default: () => []
}
}
})
new Vue({
el: '#app',
data() {
return {
users: [
{ id: 1, name: 'foo' },
{ id: 2, name: 'bar' },
]
}
},
methods: {
editFunction(id) {
console.warn('edit item ' + id)
},
deleteFunction(id) {
console.warn('delete item ' + id)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<table border="1">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>actions</tr>
</tr>
</thead>
<tbody>
<tr v-for="user in users" :key="user.id">
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>
<dropdown
:items="[
{
text: 'Edit',
icon: 'fal fa-edit',
event: 'edit-function'
},
{
text: 'Delete',
icon: 'fal fa-trash-alt',
event: 'delete-function'
}
]"
@edit-function="editFunction(user.id)"
@delete-function="deleteFunction(user.id)"
/>
</td>
</tr>
</tbody>
</table>
</div>
<script type="text/x-template" id="dropdown-template">
<div class="dropdown">
<a class="trigger">
<i class="far fa-ellipsis-h"></i>
</a>
<ul class="items">
<li v-for="(item, index) in items" :key="index" class="item">
<a @click="$emit(item.event)">
<i :class="item.icon"></i>
{{ item.text }}
</a>
</li>
</ul>
</div>
</script>
Upvotes: 0