Nuxt.jsでカレンダープラグイン「Fullcalendar」を使ってみた

## Nuxt.jsでカレンダー予定管理機能を導入

なにかいいものはないかと探していたら、fullcalendarを見つけた。jQueryに依存していないV4のbeta版がでているとのことでそちらを今回導入してみました。
※予定では3月15日に正規版がリリースされるみたいです。

ソース
<https://github.com/sauzar18/fullcalendar-v4-nuxt>

## 環境

- [Nuxt.js](https://ja.nuxtjs.org/guide/) v2.4.5
- Express.js v4.16.4
- backpack-core
- [Fullcalendar v4 beta](https://fullcalendar.io/docs/v4)

## Nuxt.jsを導入

公式ドキュメント参照
[Nuxt.js](https://ja.nuxtjs.org/guide/)

こちらから導入方法がわかります。

backpack-coreの設定は下記参照
<https://qiita.com/sauzar18/items/32a8553587e2bdcf3689>

## 1．Fullcalendarベースのモジュールをインストール

```bash
yarn add @fullcalendar/core @fullcalendar/daygrid
```

これを導入すると下記ができるようになります。

<https://fullcalendar.io/docs/v4/daygrid-view-demo>

ここから追加したい機能に応じてモジュールをインストールしていきます。

今回は下記をインストールしました。

- moment.jsの特徴を引き継いでいるmomentプラグイン
- 時間ごとに予定が管理できるtimegridプラグイン
- データのやり取り(クリックアクションでデータをゲットするなど)interactionプラグイン

```bash
yarn add @fullcalendar/moment @fullcalendar/timegrid @fullcalendar/interaction
```

インストール完了したらvueファイルの設定

## 2. Vueファイル設定

```html:pages/index.vue
<template>
  <div>
    <main>
      <div id="calendar" />
    </main>
  </div>
</template>
<script>
export default {
  head() {
    return {
      link: [
        { rel: 'stylesheet', href: '/css/fullcalendar.min.css' }
      ]
    }
  },
  data() {
    return {
      events: [
        { title: 'event1', start: '2019-02-27' },
        { title: 'event2', start: '2019-02-28T12:00', end: '2019-02-28T14:00' },
        { title: 'event3', start: '2019-02-28T12:30:00', allDay: false }
      ]
    }
  },
  created() {
    this.$store.commit('setEvents', this.events)
  }
}
</script>
```

こんな感じでVueファイルを設定していきます。

Rest apiなどasync awaitを使ってeventsデータを読み込んだ方が実用的なので、その方法も記載

```html:pages/index.vue
<template>
  <div>
    <main>
      <div id="calendar" />
    </main>
  </div>
</template>
<script>
export default {
async asyncData ({ app, store, params }) {
    const data = await app.$axios.$get(`/api/get_schedule/${store.state.authUser.userid}`)
    store.commit('setEvents', data)
  },
  head() {
    return {
      link: [
        { rel: 'stylesheet', href: '/css/fullcalendar.min.css' }
      ]
    }
  }
}
</script>
```

ここでやっていることは

- カレンダーを表示する箇所にidを設置
- イベントデータを読み込んでstoreで状態管理
- fullcalendarのインストール版に入っているcssを読み込む

## 3. storeの設定

```js:store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
export const state = () => ({
  events: null
})
export const mutations = {
  setEvents(state, data) {
    state.events = data
  }
}
```

## 4. pluginsでfullcalendarを読み込み

```js:plugins/calendar.js
import { Calendar } from '@fullcalendar/core'
import dayGridPlugin from '@fullcalendar/daygrid'
import momentPlugin from '@fullcalendar/moment'
import timeGridPlugin from '@fullcalendar/timegrid'
import interactionPlugin from '@fullcalendar/interaction'
import jaLocale from '@fullcalendar/core/locales/ja' // 日本語に対応させるために読み込む
export default function (context) {
  if (document.querySelector('#calendar')) {
    document.addEventListener('DOMContentLoaded', function () {
      const calendarEl = document.querySelector('#calendar')
      const calendar = new Calendar(calendarEl, {
        locale: jaLocale,
        height: 625,
        events: context.store.state.events,
        plugins: [dayGridPlugin, momentPlugin, timeGridPlugin, interactionPlugin],
        defaultView: 'dayGridMonth',
        editable: true,
        selectable: true,
        selectHelper: true,
        ignoreTimezone: false,
        header: {
          left: 'today prev,next',
          center: 'title',
          right: 'dayGridMonth,timeGridWeek,timeGridDay'
        },
        eventLimit: true,
        firstDay: 0,
        weekends: true,
        weekMode: 'fixed',
        weekNumbers: false,
        slotDuration: '00:30:00',
        snapDuration: '00:15:00',
        allDaySlot: false,
        allDayText: 'allday',
        slotMinutes: 15,
        snapMinutes: 15,
        slotLabelFormat: 'H:mm',
        firstHour: 9,
        defaultTimedEventDuration: '10:00:00',
        axisFormat: 'H:mm',
        timeFormat: 'H:mm',
        minTime: '00:00:00',
        maxTime: '24:00:00',
        buttonText: {
          prev: '',
          next: '',
          prevYear: '',
          nextYear: ''
        },
        eventClick: function (info) {
          alert('Event: ' + info.event.title)
          alert('Coordinates: ' + info.jsEvent.pageX + ',' + info.jsEvent.pageY)
          alert('View: ' + info.view.type)
          // change the border color just for fun
          info.el.style.borderColor = 'red'
        },
        dateClick: function (info) {
          alert('Clicked on: ' + info.dateStr)
          alert('Coordinates: ' + info.jsEvent.pageX + ',' + info.jsEvent.pageY)
          alert('Current view: ' + info.view.type)
          info.dayEl.style.backgroundColor = 'red'
        }
      })

      calendar.render()
    })
  }
}

```

ここでやっていること

- モジュールをimport
- 日本語対応
- storeを使えるようにexport defaultをつかう
- id(#calendar)があるページのみスクリプトが着火
- 公式ドキュメント通りに各種設定

### locale

言語設置

### height

コンテンツの高さの設定

### events

イベント情報の設定

### plugins

利用するモジュールの設置

### defaultview

デフォルトの表示設定

### editable

カレンダーの予定を変更できるかどうか

### selectable

クリックしてドラッグすることで、ユーザーが複数の日またはタイムスロットを強調表示できるようにする

### selectHelper

ユーザーがドラッグしている間に「プレースホルダー」イベントを描画するかどうか。

### ignoreTimezone

UTCオフセットを無視するかどうか

### header

カレンダーの上部にあるボタンとタイトルを定義

### eventLimit

1日に表示されるイベントの数を制限。残りはポップオーバーに表示。

### firstDay

毎週始まる日の設定。
日曜日= 0、月曜日= 1、火曜日= 2など

### weekends

土曜日/日曜日の列をいずれかのカレンダービューに含めるかどうか。

### weekMode

月ビューに表示される週数を決定

### weekNumbers

週番号をカレンダーに表示するかどうか

### slotDuration

タイムスロットを表示する頻度。

### snapDuration

ドラッグされたイベントが時間軸にスナップする時間間隔。

### allDaySlot

「終日」スロットをカレンダーの上部に表示するかどうか

### allDayText

カレンダー上部の「終日」のスロットにタイトルを付けるテキスト

### slotMinutes

タイムスロットを表示する頻度（分単位）。

### snapMinutes

ドラッグされたイベントがアジェンダビューのタイムグリッドにスナップする間隔。

### slotLabelFormat

タイムスロット内に表示されるテキスト

### firstHour

スクロール区画に表示される最初の1時間を決定

### defaultTimedEventDuration

値が指定されていない時限イベントオブジェクトのフォールバック期間

### axisFormat

アジェンダビューの垂直軸に表示されるタイムテキストを決定

### timeFormat

各イベントに表示される時間テキストを決定

### minTime

毎日表示される最初のタイムスロットを決定

### maxTime

毎日表示される最後のタイムスロットを決定

### buttonText

ヘッダー/フッターのボタンに表示されるテキスト

### eventClick

ユーザーがイベントをクリックしたときに発生

※ここでquerySelectorなどをつかって値を入れてもいいし、試してないけど、storeにいれた管理してもよいかと

### dayClick

ユーザーが日付または時刻をクリックしたときに発生

他にも設定できることは沢山あります。詳しくは[Doc](https://fullcalendar.io/docs/v4)参照


## 5. nuxt.config.js設定

pluginの読み込み

```js:nuxt.config.js
plugins: [
    { src: '~/plugins/calendar.js', ssr: false }
],
```

eslintなどでfullcalendarが使えない場合

```js:nuxt.config.js
build: {
    /*
    ** You can extend webpack config here
    */
    extend(config, ctx) {
        // Run ESLint on save
        if (process.server && process.browser) {
        config.module.rules.push({
            enforce: 'pre',
            test: /\.(js|vue)$/,
            loader: 'eslint-loader',
            exclude: /(node_modules)/
        })
        }
    }
}
```

これでカレンダーが表示されれば設置完了です。

## まとめ

v4の本リリース後またソースの修正が必要になると思う

storeを使ってフロントに描写するかdocument.querySelectorなどDOMの操作を使って描写するといいかも

もっといい方法があるかもしれないのである方は共有お願いします。