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