[英]Vue.js: using computed property in method returns undefined error
I have a computed property that gets the state from vuex.我有一个从 vuex 获取 state 的计算属性。 Inside vuex the state is set using axios to get some data from my api.在 vuex 内部,使用 axios 设置 state 以从我的 api 获取一些数据。 My issue is that when I try use this computed property inside my methods I get an undefined error.我的问题是,当我尝试在我的方法中使用这个计算属性时,我得到一个未定义的错误。 This is because I try use the data before it has been set in the vuex store.这是因为我尝试使用在 vuex 存储中设置之前的数据。 So how do I make sure the boardColumnData is set before I try using it in my methods?那么在我尝试在我的方法中使用它之前,如何确保设置 boardColumnData 呢?
errors:错误:
Error in mounted hook: "TypeError: Cannot read property 'total' of undefined"
TypeError: Cannot read property 'total' of undefined
AppCharts.vue 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 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 store: 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 looks like this: 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
}
}
]
Vue should be able to handle the reactivity of updating your components once the data arrives in the store, and since you're passing it into your component correctly, I think you just need to make some small adjustments to make the component more reactive.一旦数据到达存储区,Vue 应该能够处理更新组件的反应性,并且由于您将其正确传递到组件中,我认为您只需要进行一些小调整以使组件更具反应性。 I'd move the datacollection
to a computed property, since it's only dependent on the store's boardColumnData
, and then could you move your chartOptions
to be defined initially, since it's just static data?我会将datacollection
移动到计算属性,因为它仅依赖于商店的boardColumnData
,然后您能否将您的chartOptions
移动到最初定义,因为它只是 static 数据?
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...
and then in your template, just check for if datacollection
is has a value.然后在您的模板中,只需检查datacollection
是否具有值。 For instance:例如:
<template>
<div id="chart_section">
<div id="charts" v-if="datacollection">
<DoughnutChart
:chart-data="datacollection"
:options="chartOptions"
class="chart"
/>
</div>
</div>
</template>
You can set the data before the fillData method is called by using async await on the mounted hook.您可以在调用 fillData 方法之前设置数据,方法是在挂载的钩子上使用 async await。
In AppCharts.vue, inside your mounted() hook call getAllBoardPostData()在 AppCharts.vue 中,在您的 mounted() 挂钩调用 getAllBoardPostData()
`async mounted() {
await this.$store.dispatch('getAllBoardPostData')
this.fillData();
},`
This will fetch the data from your api and populate your store when the component is loaded, and before the fillData() method is called这将从您的 api 获取数据并在加载组件时填充您的存储,并且在调用 fillData() 方法之前
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.