【Vue +  ApexCharts】データを集計して円グラフに表示させる

#はじめに
皆さんおはこんばにちは。
エンジニア歴１年目、現場経験ゼロの弱小自称 Web エンジニアです。

Vue のアプリケーション開発で、見た目がシャレ乙な ApexCharts を導入してみたは良いものの
どうやってデータを詰めたらいいんジャイ！！となったので
本記事を作成するにいたった次第でございます。

##やりたいこと
・ApexChartsを使って円グラフを表示させる。
・ストアに格納したデータの集計をその円グラフに反映させる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/899358/0457cb0a-475a-7237-0854-6f3adb83417a.png)

##環境
・Vue CLI v3.5.0
・Node v12.18.4
・Npm 6.14.6
・Vuetify 2.3.18
・ApexCharts 1.6.0

##コードサンプル
本記事の目的は用意されたデータを如何にして ApexCharts に適用させるかなので
Vueプロジェクトの作成方法やデータの格納方法などは割愛します。

もし、プログラムを実際に動かしてみて確認したい方がいましたら
以下のコードサンプルをご活用ください。

<details><summary>Vuetifyの導入に必要なターミナルコマンド</summary><div>

```terminal
$ vue add vuetify
```

</div></details>
<details><summary>App.vue</summary><div>

```vue

<template>
  <v-app>
    <v-main>
      <router-view />
    </v-main>
  </v-app>
</template>
```

</div></details>
<details><summary>Form.vue</summary><div>

```vue
<template>
    <v-layout row wrap justify-center>
        <v-flex xs5 mt-5>
            <v-card>
                <v-card-title>
                    <v-flex xs12 class="text-center">
                        支出フォーム
                    </v-flex>
                </v-card-title>

                <v-divider></v-divider>

                <v-card-text>
                    <v-form ref="test_form">
                        <v-text-field v-model="expenditure.price" label="支出額：" :rules="[numberRules.required,numberRules.regex]">
                            <template v-slot:append>円</template>
                        </v-text-field>

                        <v-radio-group row label="カテゴリー：" v-model="expenditure.category">
                            <v-radio label="食費" value="食費"></v-radio>
                            <v-radio label="交通費" value="交通費"></v-radio>
                            <v-radio label="住居費" value="住居費"></v-radio>
                            <v-radio label="その他" value="その他"></v-radio>
                        </v-radio-group>
                    </v-form>
                </v-card-text>

                <v-divider></v-divider>

                <v-card-actions>
                    <v-flex class="text-center">
                        <v-btn dark depressed @click="submit" color="info">入力</v-btn>
                    </v-flex>
                </v-card-actions>
            </v-card>
        </v-flex>
    </v-layout>
</template>

<script>
import { mapActions } from 'vuex'

export default {
    data() {
        return {
            //フォームに入力された値
            expenditure:{},
            //入力規則
            numberRules: {
                required: value => !!value || "未入力は許さない",
                regex: value => /^[0-9０-９]+$/.test(value) || "数字だけ入力してちょ",
            },
        }
    },

    methods: {
        submit() {
            if (this.$refs.test_form.validate()) {
                this.expenditure.price = Number(this.expenditure.price); //入力された支出額を数値に変換
                this.addExpenditure(this.expenditure); //actions の addExpenditure を呼び出し
                this.$router.push({name:'Chart'}); //ルーティング
                this.expenditure = {}; //空に戻す
            }
        },
        ...mapActions(["addExpenditure"]),
    }
}
</script>
```

</div></details>
<details><summary>Chart.vue</summary><div>

```vue
<template>
    <v-layout row wrap justify-center>
        <v-flex xs5 mt-5>
            <v-card>
                <v-card-title>
                    <v-flex xs12 class="text-center">
                        円グラフ
                    </v-flex>
                </v-card-title>

                <v-divider></v-divider>

                <v-card-text>
                    <!-- ここに円グラフを表示させるためのタグを書く -->
                    
                </v-card-text>

                <v-divider></v-divider>

                <v-card-actions>
                    <v-flex class="text-center">
                        <v-btn dark depressed @click="returnPage" color="info">戻る</v-btn>
                    </v-flex>
                </v-card-actions>
            </v-card>
        </v-flex>
    </v-layout>
</template>

<script>
export default {
    methods: {
        returnPage() {
            this.$router.push({name:'Form'}); //ルーティング
        }
    }
}
</script>
```

</div></details>
<details><summary>router/index.js</summary><div>

```javascript
import Vue from 'vue'
import VueRouter from 'vue-router'

import Form from '../views/Form.vue'
import Chart from '../views/Chart.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Form',
    component: Form
  },
  {
    path: '/chart',
    name: 'Chart',
    component: Chart
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router
```

</div></details>
<details><summary>store/index.js</summary><div>

```javascript
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    expenditures:[], //支出データを保存する配列
  },
  mutations: {
    //支出データを state の expenditures に追加
    addExpenditure(state, expenditure) {
      state.expenditures.push(expenditure)
    }
  },
  actions: {
    //支出データを追加する mutations の呼び出し
    addExpenditure({commit}, expenditure) {
      commit('addExpenditure', expenditure)
    }
  },
  getters: {
  }
})
```

</div></details>

#ApexCharts のインストール

```terminal
$ npm install vue-apexcharts
```

#円グラフの表示
まずは以下の記事を参考に円グラフを表示させてみます。
[円グラフのコードサンプル](https://qiita.com/trustbank_kei/items/5c62a52e9de043392ebb#%E5%86%86%E3%82%B0%E3%83%A9%E3%83%95%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%89%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB)

```html:Chart.vue
<apexchart type="pie" height="400" :options="chart.options" :series="chart.series"></apexchart>
```

```javascript
<script>
import VueApexCharts from 'vue-apexcharts'

export default {
    components: {
        apexchart: VueApexCharts,
    },

    data: () => ({
        chart: {
            options: {
                labels: ['食費','交通費','住居費','その他'],
                title: {
                    text: 'ジャンル別 支出割合',
                    align: 'center',
                },
            },
            series: [4, 2, 5, 3],
        }
    }),
```

`series`は、データのオブジェクトの配列を受け付けるオプションです。
`labels`は、`series`の配列に対応した配列を受け付けるオプション。

今回の場合であれば
食費、交通費、住居費、その他の割合が **４：２：５：３** の円グラフが表示されます。

#算出されたデータの表示
本題はここから。

先程は適当な数値を入れて円グラフを表示させましたが
自前で用意したデータの集計を表示させたい。

その場合は、加工したデータを配列に詰めちゃえばいいわけです。
（結局`series`は配列を受け付けるので。）

今回は state 内のデータを加工するので getters 内で算出処理をする。

```javascript:store/index.js
getters: {
  chartSeries: (state) => {
    let foodSum = 0; //食費支出額の合計
    let transportationSum = 0; //交通費支出額の合計
    let housingSum = 0; //住居費支出額の合計
    let othersSum = 0; //その他の支出額の合計

    for (let ex of state.expenditures) {
      if (ex.category === '食費') {
        foodSum += ex.price;
      } else if (ex.category === '交通費') {
        transportationSum += ex.price;
      } else if (ex.category === '住居費') {
        housingSum += ex.price;
      } else if (ex.category === 'その他') {
        othersSum += ex.price;
      }
    }

    return [foodSum, transportationSum, housingSum, othersSum]; //それぞれの合計が詰まった配列
  }
}
```

後は、`series: [4, 2, 5, 3]`を消して以下のように書き換えれば、ほい完成。

```html:Chart.vue
<apexchart type="pie" height="400" :options="chart.options" :series="$store.getters.chartSeries"></apexchart>
```

#参考記事
・[Vue.jsでApexchartsを使ってグラフ描画を行う](https://qiita.com/trustbank_kei/items/5c62a52e9de043392ebb)