29
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Vuetifyで簡単にカレンダーを表示する

Last updated at Posted at 2020-08-15

CSSやJavascriptでカレンダーを作るの、難しくないですか?
Vuetifyのカレンダーコンポーネントを使うと簡単にカレンダーを作ることができたので、作り方を紹介したいと思います

ゴール

↓のようなカレンダーを作ります
https://vuetify-calendar-sample.netlify.app/
スクリーンショット 2020-08-15 21.27.58.png

ソースコード

Vuetify

VuetifyはVueで使えるUIライブラリの一つです
自分でCSSを記述することなく美しいUIを作ることができます
今回はVue CLIを使って簡単なカレンダーのデモを作成していきます

環境

  • Node.js v14.8.0
  • VueCLI 4.5.3

nodeのインストール

nodenvを使ってnodeをインストールしました。nodenvのインストールはこちらを参考にしてください

$ nodenv install 14.8.0
$ nodenv global 14.8.0
$ nodenv local 14.8.0
$ node -v
v14.8.0

VueCLIのインストール

公式ガイドを参考にインストールします

$ npm install -g @vue/cli@4.5.3
$ vue --version
@vue/cli 4.5.3

プロジェクトの作成

VueCLIコマンドでプロジェクトを作成します

$ vue creare calendar-app

プリセットはDefault(Vue2)を選択しました (Vue3で試してみましたがVuetifyのインストール時にエラーが出ました)
スクリーンショット 2020-08-15 17.32.06.png

Vuetifyのインストール

プロジェクトディレクトリに移動してVuetifyをインストールします

$ cd calendar-app
$ vue add vuetify

プリセットはDefaultを選択しました
スクリーンショット 2020-08-15 17.33.52.png
インストールが終わるとsrc/main.jsにVuetifyの設定が自動で記述されます

 import Vue from 'vue'
 import App from './App.vue'
+import vuetify from './plugins/vuetify';

 Vue.config.productionTip = false

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

ここまでで表示してみると下の画像のようにVuetifyのウェルカム画面が表示されます

npm run serve
# http://localhost:8080/ を開く

スクリーンショット 2020-08-16 2.19.19.png

カレンダーを表示する

まず、src/App.vueを次のように書き換えます

src/App.vue
<template>
  <v-app>
    <v-main>
      <Calendar />
    </v-main>
  </v-app>
</template>

<script>
import Calendar from './components/Calendar';

export default {
  name: 'App',
  components: {
    Calendar,
  },
};
</script>

v-appコンポーネントで囲うことを忘れてしまうとVuetifyのスタイルが適用されないので注意です 参考

次に、src/components/Calendar.vueを作成します
公式ドキュメントのサンプルを見つつ、最低限表示できるところまでコードを削ぎ落としました

src/components/Calendar.vue
<template>
  <div>
    <v-sheet tile height="6vh" color="grey lighten-3" class="d-flex align-center">
      <v-btn icon @click="$refs.calendar.prev()">
        <v-icon>mdi-chevron-left</v-icon>
      </v-btn>
      <v-btn icon @click="$refs.calendar.next()">
        <v-icon>mdi-chevron-right</v-icon>
      </v-btn>
    </v-sheet>
    <v-sheet height="94vh">
      <v-calendar
        ref="calendar"
        :events="events"
        :event-color="getEventColor"
        @change="getEvents"
      ></v-calendar>
    </v-sheet>
  </div>
</template>

<script>
export default {
  data: () => ({
    events: [],
  }),
  methods: {
    getEvents() {
      const events = [
        {
          name: '会議',
          start: new Date('2020-08-03T01:00:00'), // 開始時刻
          end: new Date('2020-08-03T02:00:00'), // 終了時刻
          color: 'blue',
          timed: true, // 終日ならfalse
        },
      ];
      this.events = events;
    },
    getEventColor(event) {
      return event.color;
    },
  },
};
</script>

今回はデモ用なので直接イベントデータを記述していますが、APIからデータを取得する処理に書き換えることもできます
このようなカレンダーが表示されます

スクリーンショット 2020-08-16 2.46.15.png

<v-calendar
  ref="calendar"
  :events="events"
  :event-color="getEventColor"
  @change="getEvents"
></v-calendar>

:events="events"でカレンダーに表示するイベントデータを渡しています
events変数は最初は空配列ですが、@change="getEvents"で値を代入しています

const events = [
  {
    name: '会議',
    start: new Date('2020-08-03T01:00:00'),
    end: new Date('2020-08-03T02:00:00'),
    color: 'blue',
    timed: true,
  },
];

これがカレンダーに表示されるイベントの中身です
start, endは開始時刻と終了時刻であり、Dateオブジェクトを指定します
timedには時刻を表示する時はtrue、表示しない(終日)の時はfalseを指定します
配列にイベントオブジェクトを追加していくことでカレンダーに表示されるイベントが増えていきます

ここから機能を追加していきます

年月を表示する

表示しているカレンダーの年月を表示するようにします
Moment.jsを使うので先にインストールしておきます

$ npm install moment --save
src/components/Calendar.vue
<template>
  <div>
    <v-sheet tile height="6vh" color="grey lighten-3" class="d-flex align-center">
      <v-btn icon @click="$refs.calendar.prev()">
        <v-icon>mdi-chevron-left</v-icon>
      </v-btn>
      <v-btn icon @click="$refs.calendar.next()">
        <v-icon>mdi-chevron-right</v-icon>
      </v-btn>
+     <v-toolbar-title>{{ title }}</v-toolbar-title>
    </v-sheet>
  </div>
</template>

<script>
+ import moment from 'moment';

export default {
  data: () => ({
    events: [],
+   value: moment().format('yyyy-MM-DD'),  // 現在日時
  }),
+ computed: {
+   title() {
+     return moment(this.value).format('yyyy年 M月');  // 表示用文字列を返す
+   }
+ },
  methods: {
    ...
  },
};
</script>

value変数には2020-08-01のような形式の日付文字列が入ります
初期値は現在の日付です
title()で表示形式を変換して表示しています

月を戻す/進める

v-calendarv-model="value"を設定します
これだけで、ボタンを押すと戻る/進むできるようになります

src/components/Calendar.vue
<v-calendar
  ref="calendar"
+ v-model="value"
  :events="events"
  :event-color="getEventColor"
  @change="getEvents"
></v-calendar>

例えば戻るボタンを押すと$refs.calendar.prev()が実行されます
これが実行されるとvalueに先月の末日が代入されます
元が2020-08-15だったら2020-07-31が入ります
一方で$refs.calendar.next()は次月の初日である2020-08-01が入ります
従ってvalueに表示させたい月の日付を代入することでカレンダーを切り替えることができます

今日ボタンを追加

src/components/Calendar.vue
<template>
  <div>
    <v-sheet tile height="6vh" color="grey lighten-3" class="d-flex align-center">
+     <v-btn outlined small class="ma-4" @click="setToday">
+       今日
+     </v-btn>
      <v-btn icon @click="$refs.calendar.prev()">
        <v-icon>mdi-chevron-left</v-icon>
      </v-btn>
      ...
</template>

<script>
  ...
  methods: {
+   setToday () {
+       this.value = moment().format('yyyy-MM-DD')
+   },
    ...
</script>

今月のカレンダーに戻りたい時はvalueに今日の日付を代入すればできます👍

日本語表記

曜日の表示がSUNやMONのように英語表記になっているので、日本語表記に変えます

src/components/Calendar.vue
<v-calendar
  ref="calendar"
  v-model="value"
  :events="events"
  :event-color="getEventColor"
+ locale="ja-jp"
+ :day-format="(timestamp) => new Date(timestamp.date).getDate()"
+ :month-format="(timestamp) => (new Date(timestamp.date).getMonth() + 1) + ' /'"
  @change="getEvents"
></v-calendar>

これだけで日本語の曜日表記になります
スクリーンショット 2020-08-16 5.01.00.png

イベントのクリック処理、日付のクリック処理

イベントをクリックした時、日付をクリックした時の処理の書き方です
ここでは簡単のためalertを実行しています

src/components/Calendar.vue
<template>
  ...
      <v-calendar
        ref="calendar"
        v-model="value"
        :events="events"
        :event-color="getEventColor"
        locale="ja-jp"
        :day-format="(timestamp) => new Date(timestamp.date).getDate()"
        :month-format="(timestamp) => new Date(timestamp.date).getMonth() + 1 + ' /'"
        @change="getEvents"
+       @click:event="showEvent"
+       @click:date="viewDay"
      ></v-calendar>
  ...
</template>

<script>
  ...
  methods: {
    setToday() {
      this.value = moment().format('yyyy-MM-DD');
    },
+   showEvent({ event }) {
+     alert(`clicked ${event.name}`);
+   },
+   viewDay({ date }) {
+     alert(`date: ${date}`);
+   },
    ...
</script>

@click:event, @click:dateにそれぞれ実行させるメソッドを指定します
メソッドの引数に色々なデータを持つオブジェクトが渡されるので、それを使ってイベントの詳細やデイリーカレンダーの表示などを実行できそうです

最終的なコード

src/components/Calendar.vue
<template>
  <div>
    <v-sheet tile height="6vh" color="grey lighten-3" class="d-flex align-center">
      <v-btn outlined small class="ma-4" @click="setToday">
        今日
      </v-btn>
      <v-btn icon @click="$refs.calendar.prev()">
        <v-icon>mdi-chevron-left</v-icon>
      </v-btn>
      <v-btn icon @click="$refs.calendar.next()">
        <v-icon>mdi-chevron-right</v-icon>
      </v-btn>
      <v-toolbar-title>{{ title }}</v-toolbar-title>
    </v-sheet>
    <v-sheet height="94vh">
      <v-calendar
        ref="calendar"
        v-model="value"
        :events="events"
        :event-color="getEventColor"
        locale="ja-jp"
        :day-format="(timestamp) => new Date(timestamp.date).getDate()"
        :month-format="(timestamp) => new Date(timestamp.date).getMonth() + 1 + ' /'"
        @change="getEvents"
        @click:event="showEvent"
        @click:date="viewDay"
      ></v-calendar>
    </v-sheet>
  </div>
</template>

<script>
import moment from 'moment';

export default {
  data: () => ({
    events: [],
    value: moment().format('yyyy-MM-DD'),
  }),
  computed: {
    title() {
      return moment(this.value).format('yyyy年 M月');
    },
  },
  methods: {
    setToday() {
      this.value = moment().format('yyyy-MM-DD');
    },
    showEvent({ event }) {
      alert(`clicked ${event.name}`);
    },
    viewDay({ date }) {
      alert(`date: ${date}`);
    },
    getEvents() {
      const events = [
        // new Dateからmomentに変更
        {
          name: '会議',
          start: moment('2020-08-03 10:00:00').toDate(),
          end: moment('2020-08-03 11:00:00').toDate(),
          color: 'blue',
          timed: true,
        },
        // イベントを追加
        {
          name: '休暇',
          start: moment('2020-08-04').toDate(),
          end: moment('2020-08-04').toDate(),
          color: 'green',
          timed: false,
        },
        {
          name: '出張',
          start: moment('2020-08-05').toDate(),
          end: moment('2020-08-07').toDate(),
          color: 'cyan',
          timed: false,
        },
        {
          name: '飲み会',
          start: moment('2020-08-06').toDate(),
          end: moment('2020-08-06').toDate(),
          color: 'orange',
          timed: false,
        },
        {
          name: '打ち合わせ',
          start: moment('2020-08-07 10:00').toDate(),
          end: moment('2020-08-07 11:00').toDate(),
          color: 'cyan',
          timed: true,
        },
        {
          name: '振り返り',
          start: moment('2020-08-07 11:00:00').toDate(),
          end: moment('2020-08-07 12:00').toDate(),
          color: 'cyan',
          timed: true,
        },
        {
          name: '休暇',
          start: moment('2020-09-07').toDate(),
          end: moment('2020-09-11').toDate(),
          color: 'green',
          timed: false,
        },
      ];
      this.events = events;
    },
    getEventColor(event) {
      return event.color;
    },
  },
};
</script>

まとめ

Vuetifyを使ってカレンダーを表示する方法を紹介しました
自分でCSSやJavascriptを書くよりもずっと少ないコードで実装することができました!
もっとたくさんイベントを追加してみるとわかるのですが、連日のイベントだと横に伸びたり、イベントが多いとmore表示になったりして気が利いています。便利
スクリーンショット 2020-08-16 5.15.21.png

参考

29
29
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?