Vue.jsのカレンダライブラリv-calendarを使って、日付ごとのデータをカレンダー上に表示

# 概要
Vue.js上で動かせるカレンダーUIライブラリのv-calendarを使って、予めサーバ等から取得した日付ごとのオブジェクトをカレンダー上に突っ込む方法です。
公式ページは[こちら](https://vcalendar.io/)

# 出力結果とソース
先に、出力結果です。
後述する日付ごとのデータを、カレンダー内に表示している内容となります。

<img width="1021" alt="スクリーンショット 2019-08-26 15.34.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153208/de12de02-b92b-094f-ef46-c75dce27f727.png">

続いてこちらのソースです。
※事前に、`npm install v-calender`をつかって、v-calendarをインストールしてください。

```javascript:main.js
import Vue from 'vue'
import App from './App.vue'

import VCalendar from 'v-calendar'

Vue.use(VCalendar) 
Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')
```

```vue:App.vue
<template>
  <div id="app">
    <calendar/>
  </div>
</template>

<script>
import calendar from './components/Calendar.vue'

export default {
  name: 'app',
  components: {
    calendar
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
```

```vue:Calendar.vue
<template>
  <div>
    <v-calendar
    :columns="2"
    title-position="center"
    >
    <template slot='header-title' slot-scope='props'>
      {{props.yearLabel}}年{{props.monthLabel}}
    </template>
    <template slot='day-content' slot-scope='props'>
      <div class="cell-header">
        {{props.day.day}}
      </div>
      <div class="cell-content">
      <template 
      v-if="dateList.some(date => date.ymd === dateToYYYYMMDD(props.day.date))">
        <div 
        class="cell-content-line" 
        v-for="content in getContentFromKey(dateToYYYYMMDD(props.day.date))" 
        v-bind:key="content">
          ・{{content}}
        </div>
        </template>
      </div>
    </template>
 
    </v-calendar>
  </div>
</template>

<script>
export default {
  name: 'calendar',
  data() {
    return {
      dateList: [
        {ymd: '20190923', contents: ['髪を切る','面談','靴を買う']},
        {ymd: '20190914', contents: ['結婚式']},
        {ymd: '20190901', contents: ['妹の誕生日']},
        {ymd: '20190817', contents: ['海に行く！', '野球の練習']}
      ]
    }
  },
  computed: {
  },
  methods: {
     dateToYYYYMMDD: function(date) {
      let y = date.getFullYear()
      let m = ('00' + (date.getMonth()+1)).slice(-2)
      let d = ('00' + date.getDate()).slice(-2)
      let result = y + '' + m + '' + d
      return result
    },
    getContentFromKey: function(key) {
      const target = this.dateList.find((date) => {
        return (date.ymd === key)
      })
      return target.contents
    }
  }
}
</script>

<style scoped>
.cell-content {
  text-align:left;
  width: 70px;
  height: 50px;
  font-size: 50%;
  /* border: 1px solid #efefef; */
}
.cell-content-line {
  border-bottom: 1px solid #efefef;
}
</style>
```

# かんたんに解説
`main.js`については、Vueにv-calendarを読み込ませているだけです。
`App.vue`のtemplate内にて、今回カレンダーを作成したCalendar.vueコンポーネントを呼び出しています。
それでは、`Calendar.vue`をかんたんに解説します。

---
先にdataの部分を説明します。

```javascript
  data() {
    return {
      dateList: [
        {ymd: '20190923', contents: ['髪を切る','面談','靴を買う']},
        {ymd: '20190914', contents: ['結婚式']},
        {ymd: '20190901', contents: ['妹の誕生日']},
        {ymd: '20190817', contents: ['海に行く！', '野球の練習']}
      ]
    }

```
このオブジェクトの配列が、今回表示したいデータとします。
ymdがkeyとなり、同じ日に複数のコンテンツが有る場合を加味しています。

---
続いて、templateの部分です。


```vue
    <v-calendar
    :columns="2"
    title-position="center"
    >
```
v-calenderを呼び出す際に、プロパティを指定しています。
今回は、２列のカレンダーとし、ヘッダを中央に寄せるような設定となっています。
どのようなプロパティが指定できるのかについては、[公式](https://vcalendar.io/api/calendar.html#props)に記載されていますので、見てみてください。

---


```vue
    <template slot='header-title' slot-scope='props'>
      {{props.yearLabel}}年{{props.monthLabel}}
    </template>

```
v-calendarでは、カレンダーの中身を編集する際、パーツに応じたスコープ付きスロットを使用します。
上記のheader-titleというスコープは、カレンダーの年月日ヘッダが記載されている部分となります。
テンプレート内で`{{props.yearLabel}}年{{props.monthLabel}}`とすることで、2019年8月といったヘッダの表示に修正しています。（この記載をしないと、デフォルトのヘッダである”2019 08月”といった表示になります。)

---

```vue
    <template slot='day-content' slot-scope='props'>
      <div class="cell-header">
        {{props.day.day}}
      </div>
      <div class="cell-content">
      <template 
      v-if="dateList.some(date => date.ymd === dateToYYYYMMDD(props.day.date))">
        <div 
        class="cell-content-line" 
        v-for="content in getContentFromKey(dateToYYYYMMDD(props.day.date))" 
        v-bind:key="content">
          ・{{content}}
        </div>
        </template>
      </div>
    </template>
```
今回のキモとなる部分です。
まず、さきほどのheader-titleと同様に、今度はday-contentというスコープのスロットを設定します。day-contentは、カレンダーの日付の部分を表示する箇所となります。
[公式](https://vcalendar.io/api/calendar.html#day-content)を見ていただければわかるように、
`props.day`というのはdayオブジェクトであり、更にその中にdayという日付の文字列がはいっています。
そのため、`props.day.day`と指定することで、日付を取得しています。

```vue
v-if="dateList.some(date => date.ymd === dateToYYYYMMDD(props.day.date))"
```
の部分で、まずprops.day.dateから取得した日付オブジェクトがdateListに存在するかどうかを見ています。
存在している日付に限り、中の

```vue
        <div 
        class="cell-content-line" 
        v-for="content in getContentFromKey(dateToYYYYMMDD(props.day.date))" 
        v-bind:key="content">
          ・{{content}}
        </div>
```
が評価され、contentが描画されるようになっております。

# まとめ
v-calendarの設定をつかってカレンダーをカスタマイズしてみましたが、
他にもたくさんのプロパティやイベントが存在するので、ぜひ一度使ってみてください。

今回の記法が冗長である等、改善部分があればぜひご指摘ください。

みていただきありがとうございました。😊