Reputation: 61
I have a Vue method that subtracts expenses from income to derive disposable income.
Right now, the subtraction method fires on blur, but it should constantly fire while both the income or expenses value are updated. For example, while someone types their income into the income input, the disposable income span that models the subtraction method should update as each digit is typed. Similarly, if someone types any expense into one of the expense components, disposable income should update while the user is typing.
var budgetLine = Vue.extend({
template: `
<div>
<div class="row" v-for="item in items">
<input type="text" placeholder="Item"></input>
<input type="text" placeholder="How much?" v-model="item.qty"></input>
<button @click="addItem">+</button>
<button @click="removeItem">-</button>
</div>
<p id="result"><strong>Total:</strong> $ {{ totalQty }} </p>
</div>
`,
data: function() {
return {
items: []
};
},
watch: {
totalQty(value) {
this.$emit('update-expense', value)
}
},
computed: {
totalQty() {
return this.items.reduce((total, item) => {
return total + Number(item.qty);
}, 0);
},
},
methods: {
addItem() {
this.items.push({
qty: 0
});
},
removeItem(item) {
this.items.pop(item);
}
},
mounted() {
this.addItem()
}
});
var budgetApp = new Vue({
el: '#app',
data: {
budgets: {
'One': 0,
'Two': 0,
'Three': 0
},
form: {
income: 0,
expenses: 0,
dispIncome: 0
}
},
components: {
'budget-line': budgetLine
},
watch: {
budgets: {
deep: true,
handler() {
this.form.expenses = this.budgetKeys.reduce((accum, key) => accum + this.budgets[key], 0)
}
}
},
computed: {
budgetKeys() {
return Object.keys(this.budgets)
},
},
methods: {
updateIncome(event) {
this.form.income = event.target.value;
this.form.dispIncome = this.form.income - this.form.expenses
},
updateExpenses(event) {
this.form.expenses = event.target.value;
this.form.dispIncome = this.form.income - this.form.expenses
},
calculateExpense(amount, budget) {
this.budgets[budget] = amount;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<header>
<div class="header_container">
<div class="header_container-copy">
<h1>Let's talk budget.</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
<div class="header_container-results">
<h2>Disposable Income</h2>
<div class="row"><span>Income:</span><span>{{ form.income }}</span></div>
<div class="row"><span>Expenses:</span><span>{{ form.expenses }}</span></div>
<div class="row"><span>Disposable Income:</span><span>{{ form.dispIncome }}</span></div>
</div>
</div>
</header>
<h2>Income</h2>
<input v-model="form.income" @change="updateIncome" type="number" class="form-control" name="income" id="income" placeholder="Income">
<h2>Expenses</h2>
<div class="budget" v-for="budget in budgetKeys">
<h3>{{budget}}</h3>
<budget-line v-on:update-expense="calculateExpense($event, budget)"></budget-line>
</div>
</div>
Upvotes: 0
Views: 1217
Reputation: 9200
You're almost there, just need to watch for changes of this form.income
model instead of updating the value by method.
var budgetLine = Vue.extend({
template: `
<div>
<div class="row" v-for="item in items">
<input type="text" placeholder="Item"></input>
<input type="text" placeholder="How much?" v-model="item.qty"></input>
<button @click="addItem">+</button>
<button @click="removeItem">-</button>
</div>
<p id="result"><strong>Total:</strong> $ {{ totalQty }} </p>
</div>
`,
data: function () {
return {
items: []
};
},
watch: {
totalQty(value) {
this.$emit('update-expense', value)
}
},
computed: {
totalQty() {
return this.items.reduce((total, item) => {
return total + Number(item.qty);
}, 0);
},
},
methods: {
addItem() {
this.items.push({
qty: 0
});
},
removeItem(item) {
this.items.pop(item);
}
},
mounted() {
this.addItem()
}
});
var budgetApp = new Vue({
el: '#app',
data: {
budgets: {
'One': 0,
'Two': 0,
'Three': 0
},
form: {
income: 0,
expenses: 0,
dispIncome: 0
}
},
components: {
'budget-line': budgetLine
},
watch: {
budgets: {
deep: true,
handler(budget) {
this.form.expenses = this.budgetKeys.reduce((accum, key) => accum + this.budgets[key], 0);
}
},
'form.income'() {
this.updateIncome();
}
},
computed: {
budgetKeys() {
return Object.keys(this.budgets)
},
},
methods: {
updateIncome() {
this.form.dispIncome = this.form.income - this.form.expenses;
},
calculateExpense(amount, budget) {
this.budgets[budget] = this.form.expenses = amount;
this.updateIncome();
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<header>
<div class="header_container">
<div class="header_container-copy">
<h1>Let's talk budget.</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
<div class="header_container-results">
<h2>Disposable Income</h2>
<div class="row"><span>Income:</span> <span>{{ form.income }}</span></div>
<div class="row"><span>Expenses:</span> <span>{{ form.expenses }}</span></div>
<div class="row"><span>Disposable Income:</span> <span>{{ form.dispIncome }}</span></div>
</div>
</div>
</header>
<h2>Income</h2>
<input v-model="form.income" type="number" class="form-control" name="income" id="income" placeholder="Income">
<h2>Expenses</h2>
<div class="budget" v-for="budget in budgetKeys">
<h3>{{budget}}</h3>
<budget-line v-on:update-expense="calculateExpense($event, budget)"></budget-line>
</div>
</div>
Upvotes: 1
Reputation: 750
first i think that the values ( Income, Expenses, Disposable Income) span will must be a computed property no a data , since they are values that are being calculated and rendered. Anyway, as you already have it if you want a quick solution so that it works, you should implement :
v-on:keyup="updateIncome"
into your input v-model="form.income" near the @change function
var budgetLine = Vue.extend({
template: `
<div>
<div class="row" v-for="item in items">
<input type="text" placeholder="Item"></input>
<input type="text" placeholder="How much?" v-model="item.qty"></input>
<button @click="addItem">+</button>
<button @click="removeItem">-</button>
</div>
<p id="result"><strong>Total:</strong> $ {{ totalQty }} </p>
</div>
`,
data: function() {
return {
items: []
};
},
watch: {
totalQty(value) {
this.$emit('update-expense', value)
}
},
computed: {
totalQty() {
return this.items.reduce((total, item) => {
return total + Number(item.qty);
}, 0);
},
},
methods: {
addItem() {
this.items.push({
qty: 0
});
},
removeItem(item) {
this.items.pop(item);
}
},
mounted() {
this.addItem()
}
});
var budgetApp = new Vue({
el: '#app',
data: {
budgets: {
'One': 0,
'Two': 0,
'Three': 0
},
form: {
income: 0,
expenses: 0,
dispIncome: 0
}
},
components: {
'budget-line': budgetLine
},
watch: {
budgets: {
deep: true,
handler() {
this.form.expenses = this.budgetKeys.reduce((accum, key) => accum + this.budgets[key], 0)
}
}
},
computed: {
budgetKeys() {
return Object.keys(this.budgets)
},
},
methods: {
updateIncome(event) {
this.form.income = event.target.value;
this.form.dispIncome = this.form.income - this.form.expenses
},
updateExpenses(event) {
this.form.expenses = event.target.value;
this.form.dispIncome = this.form.income - this.form.expenses
},
calculateExpense(amount, budget) {
this.budgets[budget] = amount;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<header>
<div class="header_container">
<div class="header_container-copy">
<h1>Let's talk budget.</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
<div class="header_container-results">
<h2>Disposable Income</h2>
<div class="row"><span>Income:</span><span>{{ form.income }}</span></div>
<div class="row"><span>Expenses:</span><span>{{ form.expenses }}</span></div>
<div class="row"><span>Disposable Income:</span><span>{{ form.dispIncome }}</span></div>
</div>
</div>
</header>
<h2>Income</h2>
<input v-model="form.income" v-on:keyup="updateIncome" @change="updateIncome" type="number" class="form-control" name="income" id="income" placeholder="Income">
<h2>Expenses</h2>
<div class="budget" v-for="budget in budgetKeys">
<h3>{{budget}}</h3>
<budget-line v-on:update-expense="calculateExpense($event, budget)"></budget-line>
</div>
</div>
Upvotes: 0