Help us understand the problem. What is going on with this article?

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

概要

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

出力結果とソース

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

スクリーンショット 2019-08-26 15.34.23.png

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

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')
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>
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の部分を説明します。

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

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


続いて、templateの部分です。

    <v-calendar
    :columns="2"
    title-position="center"
    >

v-calenderを呼び出す際に、プロパティを指定しています。
今回は、2列のカレンダーとし、ヘッダを中央に寄せるような設定となっています。
どのようなプロパティが指定できるのかについては、公式に記載されていますので、見てみてください。


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

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


    <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は、カレンダーの日付の部分を表示する箇所となります。
公式を見ていただければわかるように、
props.dayというのはdayオブジェクトであり、更にその中にdayという日付の文字列がはいっています。
そのため、props.day.dayと指定することで、日付を取得しています。

v-if="dateList.some(date => date.ymd === dateToYYYYMMDD(props.day.date))"

の部分で、まずprops.day.dateから取得した日付オブジェクトがdateListに存在するかどうかを見ています。
存在している日付に限り、中の

        <div 
        class="cell-content-line" 
        v-for="content in getContentFromKey(dateToYYYYMMDD(props.day.date))" 
        v-bind:key="content">
          ・{{content}}
        </div>

が評価され、contentが描画されるようになっております。

まとめ

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

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

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away