I am creating an app for tracking expenses.
Here is a codepen with all of the source code.
I have a global object called object
that I render out to a Mustache template:
var object = {
info : [
{date: "14, July", day: "Saturday", item: "Lunch", price: 40}
{date: "15, July", day: "Sunday", item: "Airport", price: 80}
{date: "14, July", day: "Saturday", item: "Snacks", price: 25}
],
withdrew: 0
};
{{#info}}
<tr>
<td>{{date}}</td>
<td>{{day}}</td>
<td>{{item}}</td>
<td>{{price}} /-</td>
<td><a href="#" id="editInfo">Edit</a> | <a href="#" id="deleteInfo">Delete</a></td>
</tr>
{{/info}}
Mustache.render(template, object);
As the user enters details in the Add Item div (to the right of the screenshot), the info
array in object
gets filled (it currently has 3 entries).
The part I'm stuck on is editing a row when the user clicks either the "Edit" or "Delete" button in the "MODIFY" table column. I have the following click listener bound to the table:
var modifyBtn = document.querySelector('table');
modifyBtn.addEventListener('click', function(e){
console.log(e.target);
});
With this, I am able to get both of the button nodes correctly with console.log()
, but how do I make them point uniquely to the original array elements that each row is generated from?
Any help is highly appreciated.
There are a couple of ways to do this, but the way that immediately stands out to me is the following:
data-
attributes in the HTMLinfo
array using this dataLet's run through this.
First things first, we want to retrieve the array indexes in the HTML. This data isn't available by default when rendering with Mustache, so we need to add it in ourselves. This can't be a permanent addition to the array objects. The actual indexes can change at any time (say, if an element is removed). Instead, we only want to render out the indexes for the HTML and nowhere else.
Create the following addIndexes()
function:
function addIndexes(object) {
object = JSON.parse(JSON.stringify(object));
var info = object.info;
for (var i = 0; i < info.length; i++) {
info[i].index = i;
}
return object;
}
index
.var object = {
info : [
{date: "14, July", day: "Saturday", item: "Lunch", price: 40},
{date: "15, July", day: "Sunday", item: "Airport", price: 80},
{date: "14, July", day: "Saturday", item: "Snacks", price: 25}
],
withdrew: 0
};
function addIndexes(object) {
object = JSON.parse(JSON.stringify(object));
var info = object.info;
for (var i = 0; i < info.length; i++) {
info[i].index = i;
}
return object;
}
console.log("--- The object AFTER addIndexes() ---");
console.log(addIndexes(object));
console.log("--- The object BEFORE addIndexes() ---");
console.log(object);
Now, simply modify the Mustache.render()
method to use our new addIndexes()
function:
function refreshDOM() {
var outputBody = Mustache.render(template, addIndexes(object));
...
}
data-
attributes in the HTMLNow we need to attach those indexes in our HTML template. In your main index.html
, go to the render function and change the button code to read this:
<td><a href="#" id="editInfo" data-index="{{index}}">Edit</a> | <a href="#" id="deleteInfo" data-index="{{index}}">Delete</a></td>
Now we are outputting the index to the data-index
attribute, which we can retrieve later. This renders out each button element to look something like this:
<td><a href="#" id="editInfo" data-index="0">Edit</a> | <a href="#" id="deleteInfo" data-index="0">Delete</a></td>
<td><a href="#" id="editInfo" data-index="1">Edit</a> | <a href="#" id="deleteInfo" data-index="1">Delete</a></td>
And so on, for each row.
We can now actually handle this data. Change your modifyBtn
click event listener to be the following:
var modifyBtn = document.querySelector('table');
modifyBtn.addEventListener('click', function(e){
var el = e.target;
var action = el.id;
var index = parseInt(el.dataset.index);
switch (action) {
case "editInfo": editInfo(index); break;
case "deleteInfo": deleteInfo(index); break;
}
});
el
(which is just the element) from event.target
.el.id
.data-index
which can be retrieved with el.dataset.index
. Note that this gets placed in HTML as a string (which we can't use), so we have to run parseInt()
on it to get an integer from the string.Neither of these handler functions (editInfo()
nor deleteInfo()
) are currently declared, so on to the final step!
info
array using this dataI don't know what you want to do with the editInfo
ID, so I'll leave that one up to you. I've created a simple deleteInfo
function directly above the click listener:
function deleteInfo(index) {
object.info.splice(index, 1);
refreshDOM();
}
This will remove the specified index from the global object.info
array and then call refreshDOM()
to update.
We're now complete! Sorry this was such a long-winded answer, I wanted to break down each step as we went along. Let me know if this all made sense and if you have any questions!