将方法值传递给另一个组件

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();

所以在您的子组件中,(IncomeListOutputList)您导入总线:

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 中使用 sumValuesumValueOutput,只要它们在 IncomeListOutputList.

这里有一个 sandbox 演示此解决方案。

但是,此方法有一个缺点。假设您的 App.vue 看起来像这样:

// App.vue
<template>
  <div id="app">
    <IncomeList />
    <OutputList />
    <WinTotal />
  </div>
</template>

在这种情况下,IncomeListOutputList 都在 之前 WinTotal 呈现,因此 sumValuesumValue 的初始值sumValueOutput 之前 发出 WinTotal 甚至已经开始监听这些事件。因此,WinTotal 永远不会收到 sumValuesumValueOutput 的初始值(见下文)。

如您所见,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 帮助器创建具有 sumValuesumValueOutput:

动态更新值的计算属性
<template>...</template>
<script>
import { mapState } from "vuex";

export default {
  name: "win-total",
  computed: mapState(["sumValueIncome", "sumValueOutput"]),
};
</script>

...一切就绪!

这里有一个 sandbox 演示此解决方案。