将方法值传递给另一个组件
pass method value to another component
我的组件 IncomeList
中有一个方法,其中包含方法 sumValue
。这个方法只是将不同的数字加在一起并输出一个值,比如 3+5 = 8。在我的另一个组件 OutputList
中也是如此,使用相同的逻辑但该方法被称为 sumValueOutput
。现在我想在另一个名为 WinTotal
的组件中使用这两个值。我用 props 和 vuex 尝试了一些东西,但直到现在我还没有可用的产品,我也不知道如何开始。感谢您的帮助!
收入清单:
<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>
输出列表:
<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>
我立即想到了两种适合您的方法:
1) 使用总线传递数据
在 Vue 中,您可以很容易地在子组件和父组件之间传递数据。在子组件中,您 emit 您要作为事件发送的数据:
this.$emit('event-name', payload)
...并且,在父级中,您监听事件,并对数据做一些事情:
<child-component @event-name="doSomething($event)" />
但是在两个组件之间传递数据变得更加棘手。您可以通过 child-parent-child 策略将数据从一个组件传递到另一个组件,但这会变得乏味。相反,您可以做的是创建一个 Bus:一个 Vue 实例,您将其导入两个子组件并直接在两者之间发出事件。
一辆巴士很容易制造;只需要两行代码:
// Bus.js
import Vue from "vue";
export const Bus = new Vue();
所以在您的子组件中,(IncomeList
和 OutputList
)您导入总线:
import { Bus } from "@/Bus";
...并发出值为 sumValue
的事件(在 IncomeList
中)
sumValue() {
const sumVal = this.incomesFiltered.reduce((a, c) => a + c.value, 0);
Bus.$emit("sumvalue-change", sumVal);
return sumVal;
}
...和sumOutputValue
(在OutputList
):
sumValueOutput() {
const outputValue = this.outputsFiltered.reduce((a, c) => a + c.value, 0);
Bus.$emit("sumvalueoutput-change", outputValue);
return outputValue;
}
然后在 WinTotal.vue
中导入总线并监听这些事件,在组件的 created
钩子中:
created() {
Bus.$on("sumvalue-change", (sumvalue) => (this.sumValue = sumvalue));
Bus.$on("sumvalueoutput-change", (sumvalueoutput) => (this.sumValueOutput = sumvalueoutput));
}
您现在可以根据需要在 WinTotal
中使用 sumValue
和 sumValueOutput
,只要它们在 IncomeList
或 OutputList
.
这里有一个 sandbox 演示此解决方案。
但是,此方法有一个缺点。假设您的 App.vue
看起来像这样:
// App.vue
<template>
<div id="app">
<IncomeList />
<OutputList />
<WinTotal />
</div>
</template>
在这种情况下,IncomeList
和 OutputList
都在 之前 WinTotal
呈现,因此 sumValue
和 sumValue
的初始值sumValueOutput
在 之前 发出 WinTotal
甚至已经开始监听这些事件。因此,WinTotal
永远不会收到 sumValue
和 sumValueOutput
的初始值(见下文)。
如您所见,WinTotal
最初并未使用这些值进行更新,但当它们发生变化时,它们会得到很好的更新。解决方法是将 <WinTotal />
放在 <IncomeList />
和 <OutputList />
之前,但这并不总是可行的。当然,您可以通过其他方式解决这个问题,但实施起来可能很乏味,而且不值得麻烦。所以,这是一个替代方案。
2) 使用 VueX 传递数据
一个更简单的解决方案是只使用 VueX。我假设您已经对 VueX 有所了解,因为您已经提到您之前已经尝试过一些东西,但如果有任何不清楚的地方,请告诉我。
假设 VueX 已经设置好,在你的 store
, create values in state
for sumValueIncome
and sumValueOutput
, and mutations
中更新它们:
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: {}
});
然后在 IncomeList
中,您像之前的解决方案一样更新 sumValueIncome
(当然是 VueX
):
sumValueIncome() {
const sumValueIncome = this.incomesFiltered.reduce((a, c) => a + c.value, 0);
this.$store.commit("updatesumValueIncome", sumValueIncome);
return sumValueIncome;
}
...在 OutputList
中:
sumValueOutput() {
const outputValue = this.outputsFiltered.reduce((a, c) => a + c.value, 0);
this.$store.commit("updatesumValueOutput", outputValue);
return outputValue;
}
在 WinTotal
中,您可以使用 mapState
帮助器创建具有 sumValue
和 sumValueOutput
:
动态更新值的计算属性
<template>...</template>
<script>
import { mapState } from "vuex";
export default {
name: "win-total",
computed: mapState(["sumValueIncome", "sumValueOutput"]),
};
</script>
...一切就绪!
这里有一个 sandbox 演示此解决方案。
我的组件 IncomeList
中有一个方法,其中包含方法 sumValue
。这个方法只是将不同的数字加在一起并输出一个值,比如 3+5 = 8。在我的另一个组件 OutputList
中也是如此,使用相同的逻辑但该方法被称为 sumValueOutput
。现在我想在另一个名为 WinTotal
的组件中使用这两个值。我用 props 和 vuex 尝试了一些东西,但直到现在我还没有可用的产品,我也不知道如何开始。感谢您的帮助!
收入清单:
<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>
输出列表:
<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>
我立即想到了两种适合您的方法:
1) 使用总线传递数据
在 Vue 中,您可以很容易地在子组件和父组件之间传递数据。在子组件中,您 emit 您要作为事件发送的数据:
this.$emit('event-name', payload)
...并且,在父级中,您监听事件,并对数据做一些事情:
<child-component @event-name="doSomething($event)" />
但是在两个组件之间传递数据变得更加棘手。您可以通过 child-parent-child 策略将数据从一个组件传递到另一个组件,但这会变得乏味。相反,您可以做的是创建一个 Bus:一个 Vue 实例,您将其导入两个子组件并直接在两者之间发出事件。
一辆巴士很容易制造;只需要两行代码:
// Bus.js
import Vue from "vue";
export const Bus = new Vue();
所以在您的子组件中,(IncomeList
和 OutputList
)您导入总线:
import { Bus } from "@/Bus";
...并发出值为 sumValue
的事件(在 IncomeList
中)
sumValue() {
const sumVal = this.incomesFiltered.reduce((a, c) => a + c.value, 0);
Bus.$emit("sumvalue-change", sumVal);
return sumVal;
}
...和sumOutputValue
(在OutputList
):
sumValueOutput() {
const outputValue = this.outputsFiltered.reduce((a, c) => a + c.value, 0);
Bus.$emit("sumvalueoutput-change", outputValue);
return outputValue;
}
然后在 WinTotal.vue
中导入总线并监听这些事件,在组件的 created
钩子中:
created() {
Bus.$on("sumvalue-change", (sumvalue) => (this.sumValue = sumvalue));
Bus.$on("sumvalueoutput-change", (sumvalueoutput) => (this.sumValueOutput = sumvalueoutput));
}
您现在可以根据需要在 WinTotal
中使用 sumValue
和 sumValueOutput
,只要它们在 IncomeList
或 OutputList
.
这里有一个 sandbox 演示此解决方案。
但是,此方法有一个缺点。假设您的 App.vue
看起来像这样:
// App.vue
<template>
<div id="app">
<IncomeList />
<OutputList />
<WinTotal />
</div>
</template>
在这种情况下,IncomeList
和 OutputList
都在 之前 WinTotal
呈现,因此 sumValue
和 sumValue
的初始值sumValueOutput
在 之前 发出 WinTotal
甚至已经开始监听这些事件。因此,WinTotal
永远不会收到 sumValue
和 sumValueOutput
的初始值(见下文)。
如您所见,WinTotal
最初并未使用这些值进行更新,但当它们发生变化时,它们会得到很好的更新。解决方法是将 <WinTotal />
放在 <IncomeList />
和 <OutputList />
之前,但这并不总是可行的。当然,您可以通过其他方式解决这个问题,但实施起来可能很乏味,而且不值得麻烦。所以,这是一个替代方案。
2) 使用 VueX 传递数据
一个更简单的解决方案是只使用 VueX。我假设您已经对 VueX 有所了解,因为您已经提到您之前已经尝试过一些东西,但如果有任何不清楚的地方,请告诉我。
假设 VueX 已经设置好,在你的 store
, create values in state
for sumValueIncome
and sumValueOutput
, and mutations
中更新它们:
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: {}
});
然后在 IncomeList
中,您像之前的解决方案一样更新 sumValueIncome
(当然是 VueX
):
sumValueIncome() {
const sumValueIncome = this.incomesFiltered.reduce((a, c) => a + c.value, 0);
this.$store.commit("updatesumValueIncome", sumValueIncome);
return sumValueIncome;
}
...在 OutputList
中:
sumValueOutput() {
const outputValue = this.outputsFiltered.reduce((a, c) => a + c.value, 0);
this.$store.commit("updatesumValueOutput", outputValue);
return outputValue;
}
在 WinTotal
中,您可以使用 mapState
帮助器创建具有 sumValue
和 sumValueOutput
:
<template>...</template>
<script>
import { mapState } from "vuex";
export default {
name: "win-total",
computed: mapState(["sumValueIncome", "sumValueOutput"]),
};
</script>
...一切就绪!
这里有一个 sandbox 演示此解决方案。