In the HTML, inside the pnlItems div, add a table. The header will be styled using the CSS class header, while the AMOUNT column will be styled using the CSS class numeric.
<div id="pnlItems" class="panel">
<table>
<tr class="header">
<td width="20%">MONTH</td>
<td width="40%">NAME</td>
<td width="20%" class="numeric">AMOUNT</td>
<td width="10%"></td>
<td width="10%"></td>
</tr>
</table>
</div>
<table>
<tr class="header">
<td width="20%">MONTH</td>
<td width="40%">NAME</td>
<td width="20%" class="numeric">AMOUNT</td>
<td width="10%"></td>
<td width="10%"></td>
</tr>
</table>
</div>
header will be in bold, and numeric means that text is aligned right. That's really all there is to it.
.panel
{
float: left;
padding: 10px;
border-radius: 20px;
outline: 1px solid rgba(255, 150, 0, 0.5);
margin-right: 10px;
margin-bottom: 10px;
}
.numeric
{
text-align: right;
}
.header
{
font-weight: bold;
}
label
{
width: 5em;
font-size: 0.5em;
float: left;
}
{
float: left;
padding: 10px;
border-radius: 20px;
outline: 1px solid rgba(255, 150, 0, 0.5);
margin-right: 10px;
margin-bottom: 10px;
}
.numeric
{
text-align: right;
}
.header
{
font-weight: bold;
}
label
{
width: 5em;
font-size: 0.5em;
float: left;
}
So we've got the beginnings of a table.
Now let's add content. We want the rows to render for as many elements there are in items. We also want to set key because it's a repeated HTML element we're creating.
<div id="pnlItems" class="panel">
<table>
<tr class="header">
<td width="20%">MONTH</td>
<td width="40%">NAME</td>
<td width="20%" class="numeric">AMOUNT</td>
<td width="10%"></td>
<td width="10%"></td>
</tr>
<tr v-for="(i, index) in items" v-bind:key="index">
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
<table>
<tr class="header">
<td width="20%">MONTH</td>
<td width="40%">NAME</td>
<td width="20%" class="numeric">AMOUNT</td>
<td width="10%"></td>
<td width="10%"></td>
</tr>
<tr v-for="(i, index) in items" v-bind:key="index">
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
In here, we add the month, the name and amount. Note that the amount column is styled using the numeric CSS class.
<tr v-for="(i, index) in items" v-bind:key="index">
<td>{{ months[i.month] }}</td>
<td>{{ i.name }}</td>
<td class="numeric">{{ i.amount }}</td>
<td></td>
<td></td>
</tr>
<td>{{ months[i.month] }}</td>
<td>{{ i.name }}</td>
<td class="numeric">{{ i.amount }}</td>
<td></td>
<td></td>
</tr>
Now we can test this. When you refresh, the first item in items appears.
Now add something. Here I call it "Investment Dividend" and say it's incoming of SGD 1,500. Hey, a guy can dream.
Click the ADD button and you see it appears. So far so good.
Now add something else, an outgoing amount. Wifey's birthday isn't in March and I wish I spent only a thousand, but this is just an example.
See? Some things to correct.
- The amount appears negative. Would be nice if we could color code this.
- It appears in order of entry, which is fine until you have like 50 items.
- We also want to not show the first item.
We start off by adding these CSS classes, inText and outText. So incoming money is marked in green, and outgoing money in red.
label
{
width: 5em;
font-size: 0.5em;
float: left;
}
.inText
{
color: rgb(0, 200, 0);
}
.outText
{
color: rgb(200, 0, 0);
}
input[type=text], input[type=number], select
{
width: 10em;
padding: 0em;
}
{
width: 5em;
font-size: 0.5em;
float: left;
}
.inText
{
color: rgb(0, 200, 0);
}
.outText
{
color: rgb(200, 0, 0);
}
input[type=text], input[type=number], select
{
width: 10em;
padding: 0em;
}
Then we change the class. Instead of just styling using numeric, we use a combination of numeric and inText or outText, depending on whether the amount is positive.
<tr v-for="(i, index) in items" v-bind:key="index">
<td>{{ months[i.month] }}</td>
<td>{{ i.name }}</td>
<td v-bind:class="i.amount > 0 ? 'numeric inText' : 'numeric outText'">{{ i.amount }}</td>
<td></td>
<td></td>
</tr>
<td>{{ months[i.month] }}</td>
<td>{{ i.name }}</td>
<td v-bind:class="i.amount > 0 ? 'numeric inText' : 'numeric outText'">{{ i.amount }}</td>
<td></td>
<td></td>
</tr>
Now in the HTML, we add a conditional. This means the HTML element renders only if index is greater than 0.
<tr v-for="(i, index) in items" v-bind:key="index" v-if="index > 0">
If you retry everything, you should see that incoming and outgoing amounts are colored differently, and the first row no longer appears. But soon, we'll be doing something bigger.
In computed, add the method sortedItems(). This actually returns the sorted view of the items array.
computed: {
sortedItems: function() {
return this.items
}
},
sortedItems: function() {
return this.items
}
},
Here, we use the map() method to iterate through items and return the index and the element as a new object. This serves to preserve both the index, index and the element, item.
computed: {
sortedItems: function() {
return this.items
.map((item, index) => ({ item, index }))
}
},
sortedItems: function() {
return this.items
.map((item, index) => ({ item, index }))
}
},
And we continue by chaining on a sort() method, sorting by the month property of item. This works because we need the index... but sorting items and adding or removing from it, might change index.
computed: {
sortedItems: function() {
return this.items
.map((item, index) => ({ item, index }))
.sort((a, b) => a.item.month - b.item.month);
}
},
sortedItems: function() {
return this.items
.map((item, index) => ({ item, index }))
.sort((a, b) => a.item.month - b.item.month);
}
},
Now we'll need to change this. Instead of iterating through items, we iterate through sortedItems and we change all mentions of i to si. Since each element of sortedItems is made of index and item, if we want to refer to the element's properties, we have to refer to it as item.
<tr v-for="si in sortedItems" v-bind:key="si.index" v-if="si.index > 0">
<td>{{ months[si.item.month] }}</td>
<td>{{ si.item.name }}</td>
<td v-bind:class=" si.item.amount > 0 ? 'numeric inText' : 'numeric outText'">{{ si.item.amount }}</td>
<td></td>
<td></td>
</tr>
<td>{{ months[si.item.month] }}</td>
<td>{{ si.item.name }}</td>
<td v-bind:class=" si.item.amount > 0 ? 'numeric inText' : 'numeric outText'">{{ si.item.amount }}</td>
<td></td>
<td></td>
</tr>
We'll then add two buttons. One is an UPDATE button and will run the setCurrentIndex() method, passing in index as an argument. The other is a DELETE button that runs the removeItem() method, also passing in index as an argument.
<tr v-for="si in sortedItems" v-bind:key="si.index" v-if="si.index > 0">
<td>{{ months[si.item.month] }}</td>
<td>{{ si.item.name }}</td>
<td v-bind:class=" si.item.amount > 0 ? 'numeric inText' : 'numeric outText'">{{ si.item.amount }}</td>
<td><input type="button" value="UPDATE" @click="setCurrentIndex(si.index)" /></td>
<td><input type="button" value="DELETE" @click="removeItem(si.index)" /></td>
</tr>
<td>{{ months[si.item.month] }}</td>
<td>{{ si.item.name }}</td>
<td v-bind:class=" si.item.amount > 0 ? 'numeric inText' : 'numeric outText'">{{ si.item.amount }}</td>
<td><input type="button" value="UPDATE" @click="setCurrentIndex(si.index)" /></td>
<td><input type="button" value="DELETE" @click="removeItem(si.index)" /></td>
</tr>
There be buttons! And you may notice, if you enter a June item first and then a February item, they are now sorted properly by month regardless of what order they were entered in. The first default element from items is no longer there, filtered out by the conditional.
Create these two methods. removeItem() has a parameter, index. setCurrentIndex() has a parameter as well, val.
methods: {
addUpdateItem: function() {
this.errors.name = "";
this.errors.amount = "";
let nameValue = this.$refs.itemName.value.trim();
let amountValue = parseFloat(this.$refs.itemAmount.value.trim());
let monthValue = parseInt(this.$refs.itemMonth.value);
let errors = 0;
if (nameValue == "") { this.errors.name = "Required"; errors++; }
if (amountValue <= 0 || isNaN(amountValue)) { this.errors.amount = "Must be positive"; errors++; }
if (errors > 0) return;
if (this.$refs.itemTypeOut.checked) amountValue = amountValue * -1;
if (this.currentIndex == 0) {
this.items.push({
name: nameValue,
month: monthValue,
amount: amountValue
});
}
},
removeItem: function(index) {
},
setCurrentIndex: function(val) {
}
}
addUpdateItem: function() {
this.errors.name = "";
this.errors.amount = "";
let nameValue = this.$refs.itemName.value.trim();
let amountValue = parseFloat(this.$refs.itemAmount.value.trim());
let monthValue = parseInt(this.$refs.itemMonth.value);
let errors = 0;
if (nameValue == "") { this.errors.name = "Required"; errors++; }
if (amountValue <= 0 || isNaN(amountValue)) { this.errors.amount = "Must be positive"; errors++; }
if (errors > 0) return;
if (this.$refs.itemTypeOut.checked) amountValue = amountValue * -1;
if (this.currentIndex == 0) {
this.items.push({
name: nameValue,
month: monthValue,
amount: amountValue
});
}
},
removeItem: function(index) {
},
setCurrentIndex: function(val) {
}
}
setCurrentIndex() is straightforward - simply assign the value of val to currentIndex.
removeItem: function(index) {
},
setCurrentIndex: function(val) {
this.currentIndex = val;
}
},
setCurrentIndex: function(val) {
this.currentIndex = val;
}
removeItem() uses the splice() method to remove the element at position index in items, then resets currentIndex to 0 (just in case it was something else).
removeItem: function(index) {
this.items.splice(index, 1);
this.setCurrentIndex(0);
},
setCurrentIndex: function(val) {
this.currentIndex = val;
}
this.items.splice(index, 1);
this.setCurrentIndex(0);
},
setCurrentIndex: function(val) {
this.currentIndex = val;
}
Now, let's test this. Add this item - "Bonus A" at SGD 5,000 in May. Then add "Bonus B" at SGD 15,000 in June. Click on UPDATE for "Bonus B". setCurrentIndex() should ensure that Bonus B's details appear in the upper right! Also note that the button now says "UPDATE"! That's because currentIndex is no longer 0.
Click on DELETE for "Bonus A". The item vanishes, and setCurrentIndex() changes currentIndex back to 0, so the upper right panel changes as well.
Update the addUpdateItem() method. Before, we only handled the case for currentIndex being 0. Now if currentIndex is not 0, this means it's an update. And we update the values accordingly. The values, of course, have already been validated.
addUpdateItem: function() {
this.errors.name = "";
this.errors.amount = "";
let nameValue = this.$refs.itemName.value.trim();
let amountValue = parseFloat(this.$refs.itemAmount.value.trim());
let monthValue = parseInt(this.$refs.itemMonth.value);
let errors = 0;
if (nameValue == "") { this.errors.name = "Required"; errors++; }
if (amountValue <= 0 || isNaN(amountValue)) { this.errors.amount = "Must be positive"; errors++; }
if (errors > 0) return;
if (this.$refs.itemTypeOut.checked) amountValue = amountValue * -1;
if (this.currentIndex == 0) {
this.items.push({
name: nameValue,
month: monthValue,
amount: amountValue
});
} else {
this.items[this.currentIndex].name = nameValue;
this.items[this.currentIndex].amount = amountValue;
this.items[this.currentIndex].month = monthValue;
}
this.setCurrentIndex(0);
},
this.errors.name = "";
this.errors.amount = "";
let nameValue = this.$refs.itemName.value.trim();
let amountValue = parseFloat(this.$refs.itemAmount.value.trim());
let monthValue = parseInt(this.$refs.itemMonth.value);
let errors = 0;
if (nameValue == "") { this.errors.name = "Required"; errors++; }
if (amountValue <= 0 || isNaN(amountValue)) { this.errors.amount = "Must be positive"; errors++; }
if (errors > 0) return;
if (this.$refs.itemTypeOut.checked) amountValue = amountValue * -1;
if (this.currentIndex == 0) {
this.items.push({
name: nameValue,
month: monthValue,
amount: amountValue
});
} else {
this.items[this.currentIndex].name = nameValue;
this.items[this.currentIndex].amount = amountValue;
this.items[this.currentIndex].month = monthValue;
}
this.setCurrentIndex(0);
},
In pnlItem, we add another button. It says "NEW", and when you click on it, it sets currentIndex back to 0. And it renders only if currentIndex is greater than 0.
<p>
<label for="itemAmount">Amount</label>
<br />
<input ref="itemAmount" id="itemAmount" type="number" v-bind:value="Math.abs(items[currentIndex].amount)">
<span class="error">{{ errors.amount }}</span>
</p>
<input type="button" value="NEW" @click="setCurrentIndex(0)" v-if="currentIndex > 0" />
<input type="button" v-bind:value="currentIndex == 0 ? 'ADD' : 'UPDATE'" @click="addUpdateItem" />
<label for="itemAmount">Amount</label>
<br />
<input ref="itemAmount" id="itemAmount" type="number" v-bind:value="Math.abs(items[currentIndex].amount)">
<span class="error">{{ errors.amount }}</span>
</p>
<input type="button" value="NEW" @click="setCurrentIndex(0)" v-if="currentIndex > 0" />
<input type="button" v-bind:value="currentIndex == 0 ? 'ADD' : 'UPDATE'" @click="addUpdateItem" />
Again, add these items - "Bonus A" at SGD 5,000 in May. Then add "Bonus B" at SGD 15,000 in June. Click UPDATE for Bonus A. The NEW button appears!
Ignore that button for now. Set the amount to 8000 and click UPDATE (the one in the top right corner). It should reflect the new value in the list of items below.
Now click the UPDATE button on "Bonus B". See the NEW button appear again? What happens when you click it? That's right - it should set currentIndex to 0 and give you the "New Item" view.
Next
Showing the Financial Projection.The next part is to display items in a table, so you can see what items you added.In the HTML, inside the pnlItems div, add a table. The header will be styled using the CSS class header, while the AMOUNT column will be styled using the CSS class numeric.
<div id="pnlItems" class="panel">
<table>
<tr class="header">
<td width="20%">MONTH</td>
<td width="40%">NAME</td>
<td width="20%" class="numeric">AMOUNT</td>
<td width="10%"></td>
<td width="10%"></td>
</tr>
</table>
</div>
<table>
<tr class="header">
<td width="20%">MONTH</td>
<td width="40%">NAME</td>
<td width="20%" class="numeric">AMOUNT</td>
<td width="10%"></td>
<td width="10%"></td>
</tr>
</table>
</div>
header will be in bold, and numeric means that text is aligned right. That's really all there is to it.
.panel
{
float: left;
padding: 10px;
border-radius: 20px;
outline: 1px solid rgba(255, 150, 0, 0.5);
margin-right: 10px;
margin-bottom: 10px;
}
.numeric
{
text-align: right;
}
.header
{
font-weight: bold;
}
label
{
width: 5em;
font-size: 0.5em;
float: left;
}
{
float: left;
padding: 10px;
border-radius: 20px;
outline: 1px solid rgba(255, 150, 0, 0.5);
margin-right: 10px;
margin-bottom: 10px;
}
.numeric
{
text-align: right;
}
.header
{
font-weight: bold;
}
label
{
width: 5em;
font-size: 0.5em;
float: left;
}
So we've got the beginnings of a table.
Now let's add content. We want the rows to render for as many elements there are in items. We also want to set key because it's a repeated HTML element we're creating.
<div id="pnlItems" class="panel">
<table>
<tr class="header">
<td width="20%">MONTH</td>
<td width="40%">NAME</td>
<td width="20%" class="numeric">AMOUNT</td>
<td width="10%"></td>
<td width="10%"></td>
</tr>
<tr v-for="(i, index) in items" v-bind:key="index">
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
<table>
<tr class="header">
<td width="20%">MONTH</td>
<td width="40%">NAME</td>
<td width="20%" class="numeric">AMOUNT</td>
<td width="10%"></td>
<td width="10%"></td>
</tr>
<tr v-for="(i, index) in items" v-bind:key="index">
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
In here, we add the month, the name and amount. Note that the amount column is styled using the numeric CSS class.
<tr v-for="(i, index) in items" v-bind:key="index">
<td>{{ months[i.month] }}</td>
<td>{{ i.name }}</td>
<td class="numeric">{{ i.amount }}</td>
<td></td>
<td></td>
</tr>
<td>{{ months[i.month] }}</td>
<td>{{ i.name }}</td>
<td class="numeric">{{ i.amount }}</td>
<td></td>
<td></td>
</tr>
Now we can test this. When you refresh, the first item in items appears.
Now add something. Here I call it "Investment Dividend" and say it's incoming of SGD 1,500. Hey, a guy can dream.
Click the ADD button and you see it appears. So far so good.
Now add something else, an outgoing amount. Wifey's birthday isn't in March and I wish I spent only a thousand, but this is just an example.
See? Some things to correct.
- The amount appears negative. Would be nice if we could color code this.
- It appears in order of entry, which is fine until you have like 50 items.
- We also want to not show the first item.
We start off by adding these CSS classes, inText and outText. So incoming money is marked in green, and outgoing money in red.
label
{
width: 5em;
font-size: 0.5em;
float: left;
}
.inText
{
color: rgb(0, 200, 0);
}
.outText
{
color: rgb(200, 0, 0);
}
input[type=text], input[type=number], select
{
width: 10em;
padding: 0em;
}
{
width: 5em;
font-size: 0.5em;
float: left;
}
.inText
{
color: rgb(0, 200, 0);
}
.outText
{
color: rgb(200, 0, 0);
}
input[type=text], input[type=number], select
{
width: 10em;
padding: 0em;
}
Then we change the class. Instead of just styling using numeric, we use a combination of numeric and inText or outText, depending on whether the amount is positive.
<tr v-for="(i, index) in items" v-bind:key="index">
<td>{{ months[i.month] }}</td>
<td>{{ i.name }}</td>
<td v-bind:class="i.amount > 0 ? 'numeric inText' : 'numeric outText'">{{ i.amount }}</td>
<td></td>
<td></td>
</tr>
<td>{{ months[i.month] }}</td>
<td>{{ i.name }}</td>
<td v-bind:class="i.amount > 0 ? 'numeric inText' : 'numeric outText'">{{ i.amount }}</td>
<td></td>
<td></td>
</tr>
Now in the HTML, we add a conditional. This means the HTML element renders only if index is greater than 0.
<tr v-for="(i, index) in items" v-bind:key="index" v-if="index > 0">
If you retry everything, you should see that incoming and outgoing amounts are colored differently, and the first row no longer appears. But soon, we'll be doing something bigger.
In computed, add the method sortedItems(). This actually returns the sorted view of the items array.
computed: {
sortedItems: function() {
return this.items
}
},
sortedItems: function() {
return this.items
}
},
Here, we use the map() method to iterate through items and return the index and the element as a new object. This serves to preserve both the index, index and the element, item.
computed: {
sortedItems: function() {
return this.items
.map((item, index) => ({ item, index }))
}
},
sortedItems: function() {
return this.items
.map((item, index) => ({ item, index }))
}
},
And we continue by chaining on a sort() method, sorting by the month property of item. This works because we need the index... but sorting items and adding or removing from it, might change index.
computed: {
sortedItems: function() {
return this.items
.map((item, index) => ({ item, index }))
.sort((a, b) => a.item.month - b.item.month);
}
},
sortedItems: function() {
return this.items
.map((item, index) => ({ item, index }))
.sort((a, b) => a.item.month - b.item.month);
}
},
Now we'll need to change this. Instead of iterating through items, we iterate through sortedItems and we change all mentions of i to si. Since each element of sortedItems is made of index and item, if we want to refer to the element's properties, we have to refer to it as item.
<tr v-for="si in sortedItems" v-bind:key="si.index" v-if="si.index > 0">
<td>{{ months[si.item.month] }}</td>
<td>{{ si.item.name }}</td>
<td v-bind:class=" si.item.amount > 0 ? 'numeric inText' : 'numeric outText'">{{ si.item.amount }}</td>
<td></td>
<td></td>
</tr>
<td>{{ months[si.item.month] }}</td>
<td>{{ si.item.name }}</td>
<td v-bind:class=" si.item.amount > 0 ? 'numeric inText' : 'numeric outText'">{{ si.item.amount }}</td>
<td></td>
<td></td>
</tr>
We'll then add two buttons. One is an UPDATE button and will run the setCurrentIndex() method, passing in index as an argument. The other is a DELETE button that runs the removeItem() method, also passing in index as an argument.
<tr v-for="si in sortedItems" v-bind:key="si.index" v-if="si.index > 0">
<td>{{ months[si.item.month] }}</td>
<td>{{ si.item.name }}</td>
<td v-bind:class=" si.item.amount > 0 ? 'numeric inText' : 'numeric outText'">{{ si.item.amount }}</td>
<td><input type="button" value="UPDATE" @click="setCurrentIndex(si.index)" /></td>
<td><input type="button" value="DELETE" @click="removeItem(si.index)" /></td>
</tr>
<td>{{ months[si.item.month] }}</td>
<td>{{ si.item.name }}</td>
<td v-bind:class=" si.item.amount > 0 ? 'numeric inText' : 'numeric outText'">{{ si.item.amount }}</td>
<td><input type="button" value="UPDATE" @click="setCurrentIndex(si.index)" /></td>
<td><input type="button" value="DELETE" @click="removeItem(si.index)" /></td>
</tr>
There be buttons! And you may notice, if you enter a June item first and then a February item, they are now sorted properly by month regardless of what order they were entered in. The first default element from items is no longer there, filtered out by the conditional.
Create these two methods. removeItem() has a parameter, index. setCurrentIndex() has a parameter as well, val.
methods: {
addUpdateItem: function() {
this.errors.name = "";
this.errors.amount = "";
let nameValue = this.$refs.itemName.value.trim();
let amountValue = parseFloat(this.$refs.itemAmount.value.trim());
let monthValue = parseInt(this.$refs.itemMonth.value);
let errors = 0;
if (nameValue == "") { this.errors.name = "Required"; errors++; }
if (amountValue <= 0 || isNaN(amountValue)) { this.errors.amount = "Must be positive"; errors++; }
if (errors > 0) return;
if (this.$refs.itemTypeOut.checked) amountValue = amountValue * -1;
if (this.currentIndex == 0) {
this.items.push({
name: nameValue,
month: monthValue,
amount: amountValue
});
}
},
removeItem: function(index) {
},
setCurrentIndex: function(val) {
}
}
addUpdateItem: function() {
this.errors.name = "";
this.errors.amount = "";
let nameValue = this.$refs.itemName.value.trim();
let amountValue = parseFloat(this.$refs.itemAmount.value.trim());
let monthValue = parseInt(this.$refs.itemMonth.value);
let errors = 0;
if (nameValue == "") { this.errors.name = "Required"; errors++; }
if (amountValue <= 0 || isNaN(amountValue)) { this.errors.amount = "Must be positive"; errors++; }
if (errors > 0) return;
if (this.$refs.itemTypeOut.checked) amountValue = amountValue * -1;
if (this.currentIndex == 0) {
this.items.push({
name: nameValue,
month: monthValue,
amount: amountValue
});
}
},
removeItem: function(index) {
},
setCurrentIndex: function(val) {
}
}
setCurrentIndex() is straightforward - simply assign the value of val to currentIndex.
removeItem: function(index) {
},
setCurrentIndex: function(val) {
this.currentIndex = val;
}
},
setCurrentIndex: function(val) {
this.currentIndex = val;
}
removeItem() uses the splice() method to remove the element at position index in items, then resets currentIndex to 0 (just in case it was something else).
removeItem: function(index) {
this.items.splice(index, 1);
this.setCurrentIndex(0);
},
setCurrentIndex: function(val) {
this.currentIndex = val;
}
this.items.splice(index, 1);
this.setCurrentIndex(0);
},
setCurrentIndex: function(val) {
this.currentIndex = val;
}
Now, let's test this. Add this item - "Bonus A" at SGD 5,000 in May. Then add "Bonus B" at SGD 15,000 in June. Click on UPDATE for "Bonus B". setCurrentIndex() should ensure that Bonus B's details appear in the upper right! Also note that the button now says "UPDATE"! That's because currentIndex is no longer 0.
Click on DELETE for "Bonus A". The item vanishes, and setCurrentIndex() changes currentIndex back to 0, so the upper right panel changes as well.
Update the addUpdateItem() method. Before, we only handled the case for currentIndex being 0. Now if currentIndex is not 0, this means it's an update. And we update the values accordingly. The values, of course, have already been validated.
addUpdateItem: function() {
this.errors.name = "";
this.errors.amount = "";
let nameValue = this.$refs.itemName.value.trim();
let amountValue = parseFloat(this.$refs.itemAmount.value.trim());
let monthValue = parseInt(this.$refs.itemMonth.value);
let errors = 0;
if (nameValue == "") { this.errors.name = "Required"; errors++; }
if (amountValue <= 0 || isNaN(amountValue)) { this.errors.amount = "Must be positive"; errors++; }
if (errors > 0) return;
if (this.$refs.itemTypeOut.checked) amountValue = amountValue * -1;
if (this.currentIndex == 0) {
this.items.push({
name: nameValue,
month: monthValue,
amount: amountValue
});
} else {
this.items[this.currentIndex].name = nameValue;
this.items[this.currentIndex].amount = amountValue;
this.items[this.currentIndex].month = monthValue;
}
this.setCurrentIndex(0);
},
this.errors.name = "";
this.errors.amount = "";
let nameValue = this.$refs.itemName.value.trim();
let amountValue = parseFloat(this.$refs.itemAmount.value.trim());
let monthValue = parseInt(this.$refs.itemMonth.value);
let errors = 0;
if (nameValue == "") { this.errors.name = "Required"; errors++; }
if (amountValue <= 0 || isNaN(amountValue)) { this.errors.amount = "Must be positive"; errors++; }
if (errors > 0) return;
if (this.$refs.itemTypeOut.checked) amountValue = amountValue * -1;
if (this.currentIndex == 0) {
this.items.push({
name: nameValue,
month: monthValue,
amount: amountValue
});
} else {
this.items[this.currentIndex].name = nameValue;
this.items[this.currentIndex].amount = amountValue;
this.items[this.currentIndex].month = monthValue;
}
this.setCurrentIndex(0);
},
In pnlItem, we add another button. It says "NEW", and when you click on it, it sets currentIndex back to 0. And it renders only if currentIndex is greater than 0.
<p>
<label for="itemAmount">Amount</label>
<br />
<input ref="itemAmount" id="itemAmount" type="number" v-bind:value="Math.abs(items[currentIndex].amount)">
<span class="error">{{ errors.amount }}</span>
</p>
<input type="button" value="NEW" @click="setCurrentIndex(0)" v-if="currentIndex > 0" />
<input type="button" v-bind:value="currentIndex == 0 ? 'ADD' : 'UPDATE'" @click="addUpdateItem" />
<label for="itemAmount">Amount</label>
<br />
<input ref="itemAmount" id="itemAmount" type="number" v-bind:value="Math.abs(items[currentIndex].amount)">
<span class="error">{{ errors.amount }}</span>
</p>
<input type="button" value="NEW" @click="setCurrentIndex(0)" v-if="currentIndex > 0" />
<input type="button" v-bind:value="currentIndex == 0 ? 'ADD' : 'UPDATE'" @click="addUpdateItem" />
Again, add these items - "Bonus A" at SGD 5,000 in May. Then add "Bonus B" at SGD 15,000 in June. Click UPDATE for Bonus A. The NEW button appears!
Ignore that button for now. Set the amount to 8000 and click UPDATE (the one in the top right corner). It should reflect the new value in the list of items below.
Now click the UPDATE button on "Bonus B". See the NEW button appear again? What happens when you click it? That's right - it should set currentIndex to 0 and give you the "New Item" view.












No comments:
Post a Comment