开发者问题收集

Vue.js:在方法中使用计算属性返回未定义的错误

2020-04-21
3577

我有一个计算属性,它从 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
        }
    }
]
2个回答

一旦数据到达商店,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>

Joe Dalton
2020-04-21

您可以在 mounted 钩子上使用 async await 来在调用 fillData 方法之前设置数据。

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

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

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

Intae Lall
2020-04-21