【问题标题】:Vue.js: using computed property in method returns undefined errorVue.js:在方法中使用计算属性返回未定义的错误
【发布时间】:2020-08-04 12:52:17
【问题描述】:

我有一个从 vuex 获取状态的计算属性。在 vuex 内部,使用 axios 设置状态以从我的 api 获取一些数据。我的问题是,当我尝试在我的方法中使用这个计算属性时,我得到一个未定义的错误。这是因为我尝试使用在 vuex 存储中设置之前的数据。那么在我尝试在我的方法中使用它之前,如何确保设置 boardColumnData 呢?

错误:

Error in mounted hook: "TypeError: Cannot read property 'total' of undefined"
TypeError: Cannot read property 'total' of undefined

AppCharts.vue

<template>
    <div id="chart_section">
        <div id="charts" v-if="boardColumnData">
            <DoughnutChart :chart-data="datacollection" :options="chartOptions" class="chart"></DoughnutChart>

        <button v-on:click="fillData">Fill Data</button>
    </div>
</template>


<script>
import DoughnutChart from './DoughnutChart';
import { mapGetters } from 'vuex';

export default {
    components: {
        DoughnutChart
    },
    computed: {
        ...mapGetters(['boardColumnData']),
    },
    data() {
        return {
            datacollection: null,
            chartOptions: null
        }
    },
    mounted() {
        this.fillData();
    },
    methods: {
        fillData() {
            this.datacollection = {
                datasets: [{
                    data: [this.boardColumnData[0].total.$numberDecimal, this.boardColumnData[1].total.$numberDecimal, this.boardColumnData[2].total.$numberDecimal, this.boardColumnData[3].total.$numberDecimal],
                    backgroundColor: [
                        '#83dd1a', 
                        '#d5d814',
                        '#fdab2f',
                        '#1ad4dd'
                    ],
                    borderColor: [
                        '#83dd1a', 
                        '#d5d814',
                        '#fdab2f',
                        '#1ad4dd'
                    ],
                }]
            };

            this.chartOptions = {
                responsive: true,
                maintainAspectRatio: false,
            };
        }
    }
}
</script>

DoughtnutChart.vue

<script>
    import { Doughnut, mixins } from 'vue-chartjs';
    const { reactiveProp } = mixins;

    export default {
        extends: Doughnut,
        mixins: [reactiveProp],
        props: ['chartData', 'options'],
        mounted () {
            this.renderChart(this.chartdata, this.options)
        }
    }
</script>

vuex 商店:

import axios from 'axios';

const state = {
    defaultPosts: [],
    boardPosts: [],
    boardColumnData: [],
};

const getters = {
    boardColumnData: state => state.boardColumnData,
};

const actions = {
    getAllBoardPostData: ({commit}) => {
        function getBoardColumns() {
            return axios.get('http://localhost:5000/api/summary-board/columns');
        }

        function getBoardPosts() {
            return axios.get('http://localhost:5000/api/summary-board/posts');
        }

        axios.all([getBoardColumns(), getBoardPosts()])
            .then(axios.spread((columnData, postData) => {
                let rawPosts = postData.data;
                let columns = columnData.data;
                let posts = Array.from({length: columns.length}, () => []);

                rawPosts.forEach((post) => {
                    // If column index matches post column index value
                    if(posts[post.column_index]){
                        posts[post.column_index].push(post);
                    }
                });

                columns.forEach((column, index) => {
                    let columnTotal = 0;

                    posts[index].forEach((post) => {
                        columnTotal += post.annual_value;
                    });

                    column.total.$numberDecimal = columnTotal;
                });

                commit('setBoardColumns', columns);
                commit('setBoardPosts', posts);
                commit('setDefaultPosts', posts);
            }))
            .catch(error => console.log(error));
    }
};

const mutations = {
    setDefaultPosts: (state, payload) => {
        state.defaultPosts = payload;
    },
    setBoardPosts: (state, payload) => {
        state.boardPosts = payload;
    },
    setBoardColumns: (state, payload) => {
        state.boardColumnData = payload;
    }
};

export default {
    state,
    getters,
    actions,
    mutations
};

boardColumnData 如下所示:

[
    {
        "name": "Opportunities",
        "percentage": {
            "$numberDecimal": "0"
        },
        "total": {
            "$numberDecimal": 70269
        }
    },
    {
        "name": "Prospects",
        "percentage": {
            "$numberDecimal": "0.25"
        },
        "total": {
            "$numberDecimal": 0
        }
    },
    {
        "name": "Proposals",
        "percentage": {
            "$numberDecimal": "0.5"
        },
        "total": {
            "$numberDecimal": 5376
        }
    },
    {
        "name": "Presentations",
        "percentage": {
            "$numberDecimal": "0.75"
        },
        "total": {
            "$numberDecimal": 21480
        }
    },
    {
        "name": "Won",
        "percentage": {
            "$numberDecimal": "1"
        },
        "total": {
            "$numberDecimal": 0
        }
    },
    {
        "name": "Lost",
        "percentage": {
            "$numberDecimal": "1"
        },
        "total": {
            "$numberDecimal": 0
        }
    },
    {
        "name": "No Opportunity",
        "percentage": {
            "$numberDecimal": "1"
        },
        "total": {
            "$numberDecimal": 0
        }
    }
]

【问题讨论】:

  • boardColumnData 是从 API 中获取并设置在商店中的吗?能否请您提供商店的相关代码部分?
  • @CapitanFindus 我添加了我的 vuex 存储模块。
  • 你在哪里打电话给商店getAllBoardPostData?从商店返回一个承诺怎么样,这样你就可以确定你已经提交了setBoardColumns
  • 我使用 this.$store.dispatch('getAllBoardPostData'); 在同一页面上的 AppCharts.vue 组件之后的另一个组件中调用 getAllBoardPostData在 created() 里面。我尝试从 AppCharts.vue 组件调用它,但它没有改变任何东西。我将不得不研究我的承诺以尝试实施它们
  • @CapitanFindus 我刚刚将它移动到 created() 函数内的外部组件。所以应该在其他两个组件中的任何一个尝试使用它之前设置它,但我仍然有同样的问题

标签: javascript vue.js vuex


【解决方案1】:

一旦数据到达商店,Vue 应该能够处理更新组件的反应性,并且由于您将其正确传递到组件中,我认为您只需要进行一些小的调整以使组件更反应性的。我会将datacollection 移动到计算属性,因为它仅依赖于商店的boardColumnData,然后您能否将chartOptions 移动到最初定义,因为它只是静态数据?


export default {

    data: () => ({
      // datacollection: null,  // remove this
      chartOptions: {
        responsive: true,
        maintainAspectRatio: false,
      },
    },

  //...

  computed: {
    ...mapGetters([
      'boardColumnData'
    ]),
    datacollection() {   // Property added to computed properties
      if (this.boardColumnData.length) {   // - check for boardColumnData before using it
        return {
          datasets: [{
            data: [this.boardColumnData[0].total.$numberDecimal, this.boardColumnData[1].total.$numberDecimal, this.boardColumnData[2].total.$numberDecimal, this.boardColumnData[3].total.$numberDecimal],
            backgroundColor: [
              '#83dd1a', 
              '#d5d814',
              '#fdab2f',
              '#1ad4dd'
            ],
            borderColor: [
              '#83dd1a', 
              '#d5d814',
              '#fdab2f',
              '#1ad4dd'
            ],
          }]
        };

      } else {
        return null;
      }
    }, // end dataCollection()
  },

  //... rest of component...

然后在您的模板中,检查datacollection 是否有值。例如:

<template>
    <div id="chart_section">
        <div id="charts" v-if="datacollection">
            <DoughnutChart
              :chart-data="datacollection"
              :options="chartOptions"
              class="chart"
            />
        </div>
    </div>
</template>

【讨论】:

    【解决方案2】:

    您可以在调用fillData方法之前设置数据,方法是在mounted hook上使用async await。

    在 AppCharts.vue 中,在您的 mounted() 钩子中调用 getAllBoardPostData()

    `async mounted() {
        await this.$store.dispatch('getAllBoardPostData')
        this.fillData();
    },`
    

    这将从您的 api 获取数据并在组件加载时填充您的存储,并且在调用 fillData() 方法之前

    【讨论】:

      猜你喜欢
      • 2019-03-18
      • 1970-01-01
      • 2020-08-11
      • 2019-08-04
      • 2019-11-24
      • 2021-03-05
      • 2021-02-14
      • 2013-04-25
      • 2018-05-12
      相关资源
      最近更新 更多