i have a method in my Component IncomeList
, which contains the method sumValue
. This method just added different numbers together and output one value, like 3+5 = 8. Same in my other Component OutputList
, the same logic is used but the method is called sumValueOutput
. Now i want to use both values in another component, which is called WinTotal
. I tried something with props and vuex, but till now i have no working product and i also dont know how to start. Thanks for helping!
IncomeList:
<template>
<div class="container-income">
<button class="btn btn-info" @click="showModal">show modal</button>
<div class="hidden-container-income" id="test123">
<input type="text" class="income-input" placeholder="What needs to be done" v-model="newIncome" @keyup.enter="addincome">
<input type="text" class="value-input" placeholder="€" v-model="newIncomeValue" @keyup.enter="addValue">
<transition-group name="fade" enter-active-class="animated fadeInUp" leave-active-class="animated fadeOutDown">
<income-item v-for="income in incomesFiltered" :key="income.id" :income="income"
@removedIncome="removeincome">
</income-item>
</transition-group>
<div class="extra-container">
<div><label><input type="checkbox" style="display: none" :checked="!anyRemaining" @change="checkAllincomes"></label></div>
<div>{{ remaining }} elements</div>
</div>
<div class="sum-container">
<div><label> Total Income: </label></div>
<div>{{ sumValue }} €</div>
</div>
</div>
</div>
</template>
<script>
import IncomeItem from './IncomeItem'
export default {
name: 'income-list',
components: {
IncomeItem,
},
data () {
return {
newIncome: '',
newIncomeValue: '',
idForincome: 3,
incomes: [
{
'id': 1,
'title': 'Finish Vue Screencast',
'value': 300,
'completed': false,
'editing': false,
},
{
'id': 2,
'title': 'Take over world',
'value': 315,
'completed': false,
'editing': false,
},
{
'id': 3,
'title': 'Excellent',
'value': 313,
'completed': false,
'editing': false,
},
]
}
},
computed: {
remaining() {
return this.incomes.filter(income => !income.completed).length
},
anyRemaining() {
return this.remaining != 0
},
incomesFiltered() {
return this.incomes
},
sumValue() {
return this.incomesFiltered.reduce((a, c) => a + c.value, 0)
},
},
methods: {
addincome() {
if (this.newIncome.trim().length == 0) {
return
}
this.incomes.push({
id: this.idForincome,
title: this.newIncome,
value: this.newIncomeValue,
completed: false,
})
this.newIncome = ''
this.newIncomeValue = ''
this.this.idForincome++
},
removeincome(id) {
const index = this.incomes.findIndex((item) => item.id == id)
this.incomes.splice(index, 1)
},
checkAllincomes() {
this.incomes.forEach((income) => income.completed = event.target.checked)
},
clearCompleted() {
this.incomes = this.incomes.filter(income => !income.completed)
},
finishedEdit(data) {
const index = this.incomes.findIndex((item) => item.id == data.id)
this.incomes.splice(index, 1, data)
},
//Same for Value
addValue() {
if (this.newIncomeValue.trim().length == 0) {
return
}
this.incomes.push({
id: this.idForincome,
title: this.newIncome,
value: this.newIncomeValue,
completed: false,
})
this.newIncome = ''
this.newIncomeValue = ''
this.this.idForincome++
},
showModal () {
if (document.getElementById('test123').style.display == 'none' ) {
document.getElementById('test123').style.display = 'block';
}
else {
document.getElementById('test123').style.display = 'none'
}
},
},
};
</script>
OutputList:
<template>
<div class="container-output1">
<button class="btn btn-info1" @click="showModal">show modal</button>
<div class="hidden-container-output1" id="test1231">
<input type="text" class="output-input1" placeholder="What needs to be done" v-model="newOutput" @keyup.enter="addoutput">
<input type="text" class="value-input1" placeholder="€" v-model="newOutputValue" @keyup.enter="addValue">
<transition-group name="fade" enter-active-class="animated fadeInUp" leave-active-class="animated fadeOutDown">
<output-item v-for="output in outputsFiltered" :key="output.id" :output="output"
@removedoutput="removeOutput">
</output-item>
</transition-group>
<div class="extra-container1">
<div><label><input type="checkbox" style="display: none" :checked="!anyRemaining" @change="checkAlloutputs"></label></div>
<div>{{ remaining }} elements</div>
</div>
<div class="sum-container1">
<div><label> Total Output: </label></div>
<div>{{ sumValueOutput }} €</div>
</div>
</div>
</div>
</template>
<script>
import OutputItem from './OutputItem'
export default {
name: 'output-list',
components: {
OutputItem,
},
data () {
return {
newOutput: '',
newOutputValue: '',
idForOutput: 3,
outputs: [
{
'id': 1,
'title': 'Finish Vue Screencast',
'value': 300,
'completed': false,
'editing': false,
},
{
'id': 2,
'title': 'Take over world',
'value': 315,
'completed': false,
'editing': false,
},
{
'id': 3,
'title': 'Excellent',
'value': 311,
'completed': false,
'editing': false,
},
]
}
},
computed: {
remaining() {
return this.outputs.filter(output => !output.completed).length
},
anyRemaining() {
return this.remaining != 0
},
outputsFiltered() {
return this.outputs
},
sumValueOutput() {
var outputValue = this.outputsFiltered.reduce((a, c) => a + c.value, 0);
return outputValue;
},
},
methods: {
addOutput() {
if (this.newOutput.trim().length == 0) {
return
}
this.outputs.push({
id: this.idForOutput,
title: this.newOutput,
value: this.newOutputValue,
completed: false,
})
this.newOutput = ''
this.newOutputValue = ''
this.this.idForOutput++
},
removeOutput(id) {
const index = this.outputs.findIndex((item) => item.id == id)
this.outputs.splice(index, 1)
},
checkAlloutputs() {
this.outputs.forEach((output) => output.completed = event.target.checked)
},
clearCompleted() {
this.outputs = this.outputs.filter(output => !output.completed)
},
finishedEdit(data) {
const index = this.outputs.findIndex((item) => item.id == data.id)
this.outputs.splice(index, 1, data)
},
//Same for Value
addValue() {
if (this.newOutputValue.trim().length == 0) {
return
}
this.outputs.push({
id: this.idForOutput,
title: this.newOutput,
value: this.newOutputValue,
completed: false,
})
this.newOutput = ''
this.newOutputValue = ''
this.this.idForOutput++
},
showModal () {
if (document.getElementById('test1231').style.display == 'none' ) {
document.getElementById('test1231').style.display = 'block';
}
else {
document.getElementById('test1231').style.display = 'none'
}
}
}
}
</script>
There are two ways I can think of immediately that should work for you:
In Vue you can pass data between a child component and its parent fairly easily. In the child component you emit the data you want to send, as an event:
this.$emit('event-name', payload)
...and, in the parent, you listen for the event, and do something with the data:
<child-component @event-name="doSomething($event)" />
But passing data between two components becomes trickier. You can pass data from a component to component through a child-parent-child strategy, but that becomes tedious. Instead, what you can do is create a Bus: an instance of Vue which you import in both child components and emit events directly between the two.
A Bus is easy to make; requiring all but two lines of code:
// Bus.js
import Vue from "vue";
export const Bus = new Vue();
So in your child component(s), (IncomeList
and OutputList
) you import the Bus:
import { Bus } from "@/Bus";
...and emit an event with the value of sumValue
(in IncomeList
)
sumValue() {
const sumVal = this.incomesFiltered.reduce((a, c) => a + c.value, 0);
Bus.$emit("sumvalue-change", sumVal);
return sumVal;
}
...and sumOutputValue
(in OutputList
):
sumValueOutput() {
const outputValue = this.outputsFiltered.reduce((a, c) => a + c.value, 0);
Bus.$emit("sumvalueoutput-change", outputValue);
return outputValue;
}
Then in WinTotal.vue
, you import the Bus and listen for these events, in the component's created
hook:
created() {
Bus.$on("sumvalue-change", (sumvalue) => (this.sumValue = sumvalue));
Bus.$on("sumvalueoutput-change", (sumvalueoutput) => (this.sumValueOutput = sumvalueoutput));
}
You can now use the sumValue
and sumValueOutput
in WinTotal
however you want, and they'll be automatically updated whenever they change in either IncomeList
or OutputList
.
Here's a sandbox demonstrating this solution.
HOWEVER, there's a downside to this method. Say your App.vue
looks like this:
// App.vue
<template>
<div id="app">
<IncomeList />
<OutputList />
<WinTotal />
</div>
</template>
In this case both IncomeList
and OutputList
are rendered before WinTotal
, so the initial values of sumValue
and sumValueOutput
are emitted before WinTotal
has even begun listening for these events. Thus, WinTotal
never receives the initial values of sumValue
and sumValueOutput
(see below).
As you can see, WinTotal
isn't updated initially with the values, but when they change, they are updated fine. A workaround is to put <WinTotal />
before <IncomeList />
and <OutputList />
, but that's not always feasible. Sure you could get around this in other ways, but it'd likely be tedious to implement, and not worth the trouble. So, here's an alternative.
A simpler solution would be to just use VueX. I'm assuming you know a bit about VueX already, since you've mentioned that you've tried something with it previously, but if there's anything unclear, let me know.
Assuming VueX is set up already, in your store
, create values in state
for sumValueIncome
and sumValueOutput
, and mutations
that update them:
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
sumValueIncome: null,
sumValueOutput: null
},
mutations: {
updatesumValueIncome(state, newvalue) {
state.sumValueIncome = newvalue;
},
updatesumValueOutput(state, newvalue) {
state.sumValueOutput = newvalue;
}
},
actions: {},
modules: {}
});
Then in IncomeList
, you update sumValueIncome
like in the previous solution (but with VueX
, of course):
sumValueIncome() {
const sumValueIncome = this.incomesFiltered.reduce((a, c) => a + c.value, 0);
this.$store.commit("updatesumValueIncome", sumValueIncome);
return sumValueIncome;
}
...and in OutputList
:
sumValueOutput() {
const outputValue = this.outputsFiltered.reduce((a, c) => a + c.value, 0);
this.$store.commit("updatesumValueOutput", outputValue);
return outputValue;
}
In WinTotal
, you can use the mapState
helper to create computed properties with the dynamically updated values of sumValue
and sumValueOutput
:
<template>...</template>
<script>
import { mapState } from "vuex";
export default {
name: "win-total",
computed: mapState(["sumValueIncome", "sumValueOutput"]),
};
</script>
...and you're all set!
Here's a sandbox demonstrating this solution.