VueJs 2 click doesn't seem to work
I'm not getting any errors and it's compiling so I'm not sure what I'm doing wrong. I searched for this topic without success.
I have BulkExpenses.vue which pulls and displays some expense records, then BulkExpenses.vue is a nested component that displays each record. I want to click the trash can icon and have it send the charge id back to the parent component to run the delete script. I'm doing this in another project and it's working fine, so I'm not sure what I'm doing wrong here.
There's also a Lookup component, which is the TypeAhead, to pull the stroke to connect to the Expense, but that works.
BulkExpenses.view
<template>
<div>
<form action="#" @submit.prevent="createBulkExpense()" class="publisher bt-1 border-fade bg-white" autocomplete="off">
<table>
<thead>
<tr>
<td><input v-model="bulk_name" type="text" placeholder="Name"></td>
<td><input id="bulk_expensed_at" type="text" name="bulk_expensed_at" placeholder="Date Expense Incurred"></td>
<td><input id="bulk_type_id" type="text" name="bulk_type" placeholder="Type"></td>
<td>
<lookup
source="/api/lookup/trip"
placeholder="Search your trips"
filter-key="name"
:start-at="3"
v-on:selected-link="onSelectedTrip"
:modellink="modellink">
</lookup>
</td>
<!-- <td><input id="bulk_tags" type="text" name="bulk_tags" placeholder="Tags"></td> -->
<td><input id="bulk_vendor" type="text" name="bulk_vendor" placeholder="Vendor"></td>
<td><input id="bulk_method" type="text" name="bulk_method" placeholder="Payment Method"></td>
<td><input id="bulk_total" type="text" name="bulk_total" placeholder="Total Amount"></td>
<td><input id="bulk_paidby_user_id" type="text" name="bulk_paidby" placeholder="Paid By"></td>
<td><input id="bulk_status" type="text" name="bulk_status" placeholder="Payment Status"></td>
<td><input id="bulk_notes" type="text" name="bulk_notes" placeholder="Notes"></td>
</tr>
</thead>
<tbody>
<expense v-for="expense in expenses"
:key="expense.id"
:expense="expense"
@expense-deleted="deleteExpense($event)">
</expense>
</tbody>
</table>
</form>
</div>
</template>
<script>
// import CommentsManager from './CommentsManager.vue';
var axios = require("axios");
import lookup from './Lookup.vue';
import expense from './BulkExpense.vue';
export default {
components: {
lookup, expense
},
data: function() {
return {
modellink: {
"name": "n/a",
"description": "",
"id": null,
"model": "n/a"
},
bulk_trip: {
"name": "n/a",
"description": "",
"id": null
},
selectName: "",
bulk_name: "",
bulk_expensed_at: "",
bulk_type: "",
bulk_tags: "",
bulk_vendor: "",
bulk_method: "",
bulk_total: "",
bulk_paidby: {
name: "",
id: ""
},
bulk_status: "",
bulk_notes: "",
expense: {
id: 1,
name: "",
expensed_at: "",
type: {
id: "",
name: ""
},
trip: {
id: "",
name: ""
},
tags: [],
vendor: "",
method: "",
total: "",
paidby: {
id: "",
name: ""
},
status: {
id: "",
name: ""
},
notes: ""
},
expenses: [
{
id: 1,
name: "",
expensed_at: "",
type: {
id: "",
name: ""
},
trip: {
id: "",
name: ""
},
tags: [],
vendor: "",
method: "",
total: "",
paidby: {
id: "",
name: ""
},
status: {
id: "",
name: ""
},
notes: ""
}
]
};
},
created() {
this.fetchExpenses();
},
methods: {
// onSelectedLink: function (talink) {
// // alert(JSON.stringify(talink.description, null, 4));
// this.modellink = talink
// },
onSelectedTrip: function (talink) {
// alert(JSON.stringify(talink.description, null, 4));
this.bulk_trip = talink
this.modellink = talink
},
fetchExpenses() {
axios.get('/api/expense').then((res) => {
//alert(JSON.stringify(res.data[0], null, 4));
this.expenses = res.data;
});
},
createExpense() {
axios.post('/api/expense', {name: this.expense.name, vessel_id: Laravel.vesselId, expensed_at: this.expense.expensed_at })
.then((res) => {
this.expense.content = '';
// this.expense.user_id = Laravel.userId;
// this.task.statuscolor = '#ff0000';
this.edit = false;
this.fetchExpenses();
})
.catch((err) => console.error(err));
},
deleteExpense(expense) {
console.log(expense.id);
alert(expense.id);
axios.delete('/api/expense/' + expense.id)
.then((res) => {
this.fetchExpenses()
})
.catch((err) => console.error(err));
},
}
}
</script>
BulkExpense.view
<template>
<tr>
<td><input v-model="expense.name" type="text" name="name"></td>
<td><input v-model="expense.expensed_at"></td>
<td v-if="expense.type"><input v-model="expense.type.name"></td>
<td>
<trip-select v-bind:tripId="expense.trip_id" selectName="trip_id"></trip-select>
</td>
<td><input v-model="expense.vendor"></td>
<td><input v-model="expense.method"></td>
<td><input v-model="expense.total"></td>
<td v-if="expense.paidby"><input v-model="expense.paidby.name" ></td>
<td v-if="expense.status"><input v-model="expense.status.name" ></td>
<td><input v-model="expense.notes"></td>
<td>
<a class="text-lighter hover-light" v-on:click="deleteExpense" href="#"><i class="fas fa-trash"></i></a>
</td>
</tr>
</template>
<script>
import TripSelect from './TripSelect.vue';
import typeahead from './Typeahead.vue';
export default {
name: 'expense',
components: { TripSelect, typeahead },
props: {
bulk_name: {
type: String,
required: false
},
expense: {
required: true,
type: Object,
default: function () {
return {
id: 1,
name: "",
expensed_at: "",
type: {
id: "",
name: ""
},
trip: {
id: "",
name: ""
},
tags: [],
vendor: "",
method: "",
total: "",
paidby: {
id: "",
name: ""
},
status: {
id: "",
name: ""
},
notes: ""
}
}
}
},
data() {
return {
}
},
methods: {
deleteExpense() {
alert(this.expense.id)
this.$emit('expense-deleted', {
'id': this.expense.id,
});
}
}
}
</script>
While it may not directly answer your question, it will definitely work. Also, this solves a fundamental problem you have, which is an example of an anti-pattern.
Instead of creating another method inside the component that emits to the parent, emit the signal from the click handler in the child process. Plus, you can avoid property clutter by simply sending fees back and forth:
No need to declare it $event
because it is already available by default if needed.
<expense v-for="expense in expenses" :key="expense.id" :expense="expense" @expense-deleted="deleteExpense"></expense>
Then in your click handler, just emit
do the action and transfer the charge back:
<a class="text-lighter hover-light"
@click="$emit('expense-deleted', expense)"
href="#">
<i class="fas fa-trash"></i>
</a>
This will resolve those basic issues above and possibly resolve any issues you encounter along the way.
I also see you mixing v-on:click
and @click
, but they are the same thing, one is just shorthand for the other. I suggest just @click
for cohesion.
Finally, here is the MCVE of this method .
edit
Just in case, you can remove the promise from the existing collection after it resolves:
this.expenses.slice(this.expenses.findIndex(o => o.id === expense.id), 1)
Since our expense
collection will always be in our collection, we don't have to worry about it, so you can use a backing paper.
Alternatively, you can replace the entire array, but you need to use Vue.set
this method:
Vue.set(this, 'expenses', this.expenses.filter(o => o.id !== expense.id))