I am working on a page in a Laravel 5.7 application, that has a series of VueJS child components "Prospect" wrapped within a VueJS parent component "Prospects". The child components are table rows that each have a button to delete the row/child. The parent component has a button that deletes multiple child components.
However I am unsure as to how to get the parent component function to call the child component functions to delete the children that have been marked for deletion.
Deleting the child components one at a time from within the child component itself works fine. However I am trying to call the same Child component delete function multiple times from the loop within the parent component function deleteSelectedProspects().
From within deleteSelectedProspects() i need to access the index assigned to the child component, in order to reference the correct row in the $refs array.
How can I access the index of the child component in order to properly reference it in the $refs array inside the selectedBoxes.forEach loop?
Source code for the blade view that contains the Prospects parent component:
@extends('layouts.admin')
@section('content')
<section id="widget-grid">
<div class="row">
<article class="col-xs-12 col-sm-12 col-md-12 col-lg-12 sortable-grid ui-sortable">
<prospects inline-template class="">
<div>
<div class="jarviswidget jarviswidget-sortable" id="wid-id-1" data-widget-editbutton="false" data-widget-deletebutton="false">
<header class="ui-sortable-handle">
<div class="widget-header">
<h2><i class="fa fa-list text-orange"></i> Prospects/Leads List</h2>
</div>
<button class="btn btn-sm btn-primary d-none" @click="markSelectedProspectsContacted" id="btnMarkSelectedProspectsContacted">
<span class="fa fa-check-square-o"></span> Mark Selected as Contacted
</button>
<button class="btn btn-sm btn-danger d-none" @click="deleteSelectedProspects" id="btnDeleteSelectedProspects"><span class="fa fa-trash"></span> Delete Selected</button>
</header>
<!-- widget div-->
<div role="content">
<!-- widget content -->
<div class="widget-body">
<table id="prospectsTable" class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>
<label class="vcheck">
<input type="checkbox" id="toggleCheckAllProspects" value="1" /> <span></span>
</label>
</th>
<th>Name</th>
<th>Food Category</th>
<th>Contact</th>
<th>Admin Comments</th>
<th></th>
</tr>
</thead>
<tbody>
<tr is="prospect"
v-for="(prospect, index) in prospects"
:prospect="prospect"
:index="index"
:key="prospect.id"
ref="refIds"
@delete-prospect="removeProspect()"
@update-contacted="updateContacted(index)">
</tr>
</tbody>
</table>
</div>
<!-- end widget content -->
</div>
</div>
<!-- end jarviswidget div -->
</div>
</prospects>
</article>
</div>
</section>
@endsection
@push('js-block')
<script>
$.widget( "ui.tabs", $.ui.tabs, {
_getPanelForTab: function( tab ) {
var id = $( tab ).attr( "aria-controls" );
return $( this._sanitizeSelector( "#" + id ) );
}
});
$(function() {
$('#prospectTabs').tabs({
create: removeLoader($('#prospectTabs'))
});
$('#prospectTabsAdd').click(function() {
$('#add-tab').removeClass('d-none');
});
$('#toggleCheckAllProspects').click(function() {
$('.prospect-check').prop('checked', ($(this).is(':checked') ? true : false));
});
});
</script>
@endpush
Source code for the parent component "prospects":
<script>
export default {
data: function() {
return {
formProspect: {
inputs: {
name: '',
food_cat_id: '',
phone: '',
website: '',
contact_fname: '',
contact_lname: '',
contact_title: '',
address: '',
address2: '',
city: '',
state_id: '',
zip: '',
response_notes: ''
},
headerText: "<i class='fa fa-plus-square-o text-orange'></i> Add Prospect / Lead",
btnText: "<i class='fa fa-plus-square-o'></i> Add Prospect / Lead",
errors: false
},
formSearchProspect: {
inputs: {
keywords: '',
contacted: '',
location: '',
radius: 5
}
},
formProspectMode: 'add',
prospects: []
}
},
computed: {
},
watch: {
formProspectMode: function(newMode) {
switch(newMode) {
case 'add':
this.formProspect.headerText = "<i class='fa fa-plus-square-o text-orange'></i> Add Prospect / Lead";
this.formProspect.btnText = "<i class='fa fa-plus-square-o'></i> Add Prospect / Lead";
this.clearFormProspect();
break;
case 'edit':
this.formProspect.headerText = "<i class='fa fa-edit text-orange'></i> Edit Prospect / Lead";
this.formProspect.btnText = "<i class='fa fa-save'></i> Save Prospect / Lead";
break;
}
}
},
methods: {
fetchProspects() {
var self = this;
$.get('/prospect/fetch', function(r) {
if(r.successMsg) {
self.prospects = r.prospects;
}
})
},
searchProspects() {
var self = this;
var params = {
'keywords' : this.formSearchProspect.inputs.keywords,
'contacted' : this.formSearchProspect.inputs.contacted
};
if(this.formSearchProspect.inputs.location) {
params.location = this.formSearchProspect.inputs.location;
params.radius = this.formSearchProspect.inputs.radius;
}
$.get('/prospect/search', params, function(r) {
if(r.successMsg) {
self.prospects = r.prospects;
}
})
},
addProspect() {
var self = this;
$.ajax({
type: "POST",
dataType: "json",
async: false,
url: "/prospect/add",
data: {
name: this.formProspect.inputs.name,
food_cat_id: this.formProspect.inputs.food_cat_id,
phone: this.formProspect.inputs.phone,
website: this.formProspect.inputs.website,
contact_fname: this.formProspect.inputs.contact_fname,
contact_lname: this.formProspect.inputs.contact_lname,
contact_title: this.formProspect.inputs.contact_title,
address: this.formProspect.inputs.address,
address2: this.formProspect.inputs.address2,
city: this.formProspect.inputs.city,
state_id: this.formProspect.inputs.state_id,
zip: this.formProspect.inputs.zip,
response_notes: this.formProspect.inputs.response_notes
}, success(r) {
if(r.successMsg) {
var newProspect = self.formProspect.inputs;
newProspect.id = r.newId;
newProspect.state = r.state;
newProspect.food_cat = r.food_cat;
console.log(newProspect);
self.prospects.push(Object.assign({}, newProspect));
self.clearFormProspect();
} else if(r.errors) {
self.formProspect.errors = r.errors;
}
}
});
},
saveProspect() {
var self = this;
$.post('/prospect/edit', {
id: this.formProspect.inputs.id,
name: this.formProspect.inputs.name,
food_cat_id: this.formProspect.inputs.food_cat_id,
phone: this.formProspect.inputs.phone,
website: this.formProspect.inputs.website,
contact_fname: this.formProspect.inputs.contact_fname,
contact_lname: this.formProspect.inputs.contact_lname,
contact_title: this.formProspect.inputs.contact_title,
address: this.formProspect.inputs.address,
address2: this.formProspect.inputs.address2,
city: this.formProspect.inputs.city,
state_id: this.formProspect.inputs.state_id,
zip: this.formProspect.inputs.zip,
response_notes: this.formProspect.inputs.response_notes
}, function(r) {
if(r.successMsg) {
var savedProspect = self.prospects.filter(prospect => prospect.id == self.formProspect.inputs.id)[0];
savedProspect = Object.assign({}, self.formProspect.inputs);
self.formProspectMode = "add";
} else if(r.errors) {
self.formProspect.errors = r.errors;
}
});
},
removeProspect(index) {
this.prospects.splice(index, 1);
},
clearFormProspect() {
this.formProspect.inputs = {};
this.formProspect.errors = false;
},
deleteSelectedProspects() {
var self = this;
var selectedBoxes = self.prospects.filter(prospect => prospect.selected);
$.SmartMessageBox({
title: "<i class='fa fa-trash text-orange-dark'></i> Delete (" + selectedBoxes.length + ") Selected Prospects",
content: "Are you sure you want to delete the selected leads?",
buttons: "[No][Yes]"
}, function(e) {
if("Yes" == e) {
selectedBoxes.forEach(function(p) {
var lostChild = self.prospects.filter(prospect => prospect.id == p.id);
// HOW CAN I ACCESS THE INDEX OF THE LOST CHILD TO REFERENCE IT IN THE $refs ARRAY BELOW
// HOW CAN I ACCESS THE INDEX OF THE LOST CHILD TO REFERENCE IT IN THE $refs ARRAY BELOW
//self.$refs.refIds[lostChildIndex].deleteProspect();
});
}
});
},
markSelectedProspectsContacted() {
var self = this;
var selectedBoxes = self.prospects.filter(prospect => prospect.selected);
$.SmartMessageBox({
title: "<i class='fa fa-trash text-orange-dark'></i> Mark (" + selectedBoxes.length + ") Selected Prospects as Contacted",
content: "Are you sure you want to mark the selected leads as contacted?",
buttons: "[No][Yes]"
}, function(e) {
if("Yes" == e) {
selectedBoxes.forEach(function(p) {
/*
mark contacted
*/
});
}
});
},
updateRadiusSearchDiv() {
if($('#searchLocation').val()) {
$('#radiusSearchDiv').removeClass('d-none');
} else {
$('#radiusSearchDiv').addClass('d-none');
}
}
},
mounted() {
this.fetchProspects();
setTimeout(function() {
$('#prospectsTable').DataTable({
"columnDefs": [
{ "orderable": false, "targets": [0,4,5] }
],
"language": {
"search": "Filter Results:"
},
"dom": "ftilp"
});
$('#prospectsTable > thead > tr th:first-child').removeClass('sorting_asc');
}, 500);
$('#btnDeleteSelectedProspects, #btnMarkSelectedProspectsContacted').removeClass('d-none');
}
}
</script>
Source code for the child component "prospect":
<template>
<transition name="fade">
<tr v-if="show">
<td class="text-center align-middle">
<label class="vcheck">
<input type="checkbox" class="prospect-check" value="1" v-model="prospect.selected" /> <span></span>
</label>
</td>
<td>
<div>{{ prospect.name }}</div>
<div v-show="prospect.contacted" class="label label-success"><span class="fa fa-check-square-o"></span> Contacted!</div>
</td>
<td>{{ prospect.food_cat.title }}</td>
<td>{{ prospect.contact_fname }}<span v-if="prospect.contact_lname"> {{ prospect.contact_lname }}</span><span v-if="prospect.contact_title">, {{ prospect.contact_title }}</span></td>
<td>{{ prospect.response_notes }}</td>
<td class="text-right align-middle">
<button class="btn btn-primary" @click="updateContacted" v-show="!prospect.contacted"><span class="fa fa-check-square-o"></span> Mark As Contacted</button>
<button class="btn btn-primary" @click="editProspect"><span class="fa fa-edit"></span> Edit Lead</button>
<button class="btn btn-danger" @click="removeProspect"><span class="fa fa-trash"></span> Delete Lead</button>
</td>
</tr>
</transition>
</template>
<script>
export default {
props: ['prospect', 'index'],
data: function() {
return {
prospectData: this.prospect,
show: true,
prospectIndex: this.index
}
},
methods: {
editProspect() {
this.$parent.formProspect.inputs = this.prospectData;
this.$parent.formProspectMode = "edit";
$('#prospectTabs').tabs('option', 'active', 1);
$('#add-tab').removeClass('d-none');
window.scrollTo({ top: 0, behavior: 'smooth' });
},
updateContacted() {
var self = this;
$.SmartMessageBox({
title: "<i class='fa fa-trash text-orange-dark'></i> Mark as Contacted?",
buttons: "[No][Yes]"
}, function(e) {
if("Yes" == e) {
$.post('/prospect/updateContacted/' + self.prospectData.id, function(r) {
if(r.successMsg) {
self.$emit('update-contacted');
}
});
}
});
},
removeProspect() {
var self = this;
$.SmartMessageBox({
title: "<i class='fa fa-trash text-orange-dark'></i> Delete Prospect/Lead",
content: "Are you sure you want to delete this prospect/lead?",
buttons: "[No][Yes]"
}, function(e) {
if("Yes" == e) {
self.deleteProspect();
}
});
},
deleteProspect() {
var self = this;
$.post('/prospect/delete/' + self.prospectData.id, function(r) {
if(r.successMsg) {
self.show = false;
self.$emit('delete-prospect');
}
});
}
},
mounted() {
}
}
</script>