2022-08-08/2023-05-23

【FullCalendar】javascriptでカレンダー表示を行う

【FullCalendar】javascriptでカレンダー表示を行う

カレンダーを使用したいという時にとりあえず思い浮かぶのは「なんとしてでも全部実装は避けたい」です。

カレンダーって言語の入門で課題として取り組むイメージですよね。考慮することが多く課題の題材としては優れています。

これを実用的にするためにデータベースと連携してとなると、とても手間がかかります。

そこで使用したいのが「FullCalendar」というjavascriptのライブラリです。オープンソースで一部機能を除き無料で使える上、カスタマイズ性の高いのが特徴です。

この記事では「FullCalendar」の使用方法を説明します。

導入

この記事時点での最新バージョン(5.11.2)を使用します。

新しいバージョンが出ている場合は番号を変更してください。「FullCalendar」の右上、GitHubの猫マークの左が最新のバージョン番号です。

(CodePenを使用している場所ではCDN形式を利用しています。別形式で参考にする場合別途モジュールの読み込みが必要になる場合があります。)

ダウンロードして使う

fullcalendar-5.11.2.zip

上記からダウンロードしてアプリケーション等に配置してください。

CDNを利用する

<script src="https://cdn.jsdelivr.net/npm/fullcalendar@5.11.2/main.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fullcalendar@5.11.2/main.min.css">

jsdelivrで配信されていますので読み込みます。

NPMでインストールする

[root@localhost] #npm install fullcalendar

しっかり読み込めているか簡単なコードで確認してみましょう。

HTMLはdivにidを付けただけです。この中にカレンダーが描画されます。

FullCalendarのスクリプトを読み込むと「FullCalendar」というオブジェクトが用意されます。そのオブジェクト内「Calendar」クラスのインスタンスを生成します。「render」メソッドで最初に用意したHTML内に描画しています。

カレンダーが表示されない場合はスクリプトが読み込めているか確かめてみましょう。

表示タイプ

「FullCalendar」では良く見る月ごとのカレンダーの他にも時間が表示された週表示のもの、リスト形式なども利用することができます。

枠として月、週、日から選び、表示の仕方として日、時間と選ぶことができます。

実際見た方が分かりやすいのでそれぞれの例を下記で確認してください。

月(日)表示

週(時間)表示

リスト表示

週(日)表示

タイムゾーン

タイムゾーンはデフォルトではUTCですが、日本の標準時にもすることができます。

この機能はバックエンド側とのやり取りの仕方によっては不要な場合もあります。

言語

タイムゾーンと同様に言語も日本語へと変更することができます。

サイズ

サイズの変更も可能です。

カレンダー全体での高さやカレンダー自体での高さ、縦横比など調整可能です。

また、カレンダーの日ごとの枠も調整可能になっています。

カスタマイズ

ある程度日本語環境でも適応できそう、実際に使えそうだとわかったところでもう少し細かいと頃を見ていきます。

この項目では見た目に関する部分を取り扱います。機能的な部分は次の事例で紹介します。

ツールバー

カレンダーのタイトルの位置を変更することや、さまざまなタイプのカレンダーを表示することもできます。

また、独自のボタンも作成可能です。

  • headerToolBar・・・ヘッダーツールバーの設定。「start」「center」「end」の3カ所に設定が可能。設定可能な値は「title(タイトル)」「prev(先月へボタン)」「next(翌月へボタン)」「prevYear(昨年へボタン)」「nextYear(来年へボタン)」「today(今日へボタン)」
  • footerTooBar・・・フッターツールバーの設定。設定方法はheaderToolBarと同じ。
  • buttonText・・・ボタンのテキストを変更可能。言語で日本語にしてもここは変わらないので手動で変更する。
  • buttonIcons・・・ボタンのアイコンを指定可能。指定するアイコン名は使用しているテーマによる。(テーマについては後述)
  • customButtons・・・独自のボタンを作成可能。例では「二月戻る」「二月進む」を作成している。

テーマ

テーマをFullcalendarデフォルトのものからBootstrapのものに変更することが可能です。

bootstrapは「4」と「5」が用意されており、使用するにはCSSの読み込みが追加で必要になります。

<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css' rel='stylesheet'>
<link href='https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css' rel='stylesheet'>

日付・スロット

土日を非表示にすることや平日を非表示にすること、特定の時間のみ受け付け可にすることも可能です。

時間表示時の刻み幅やラベルも変更できます。

上記は特定の曜日を非表示にする例です。

  • weekends・・・土日表示するかどうか。
  • hiddenDays・・・配列で非表示曜日を指定可能。(日曜:0 土曜:6)
  • navLinks・・・日付をリンクにするかどうか。クリックするとその日詳細を確認できるようになる。

上記はスロットのカスタマイズ例です。

メモリ間隔を2時間間隔、メモリラベルを4時間間隔、メモリラベルを24時間表記にしています。

  • slotDuration・・・メモリ間隔を指定可能。( ’02:00:00′:2時間 ’00:30:00′:30分 )
  • slotLabelInterval・・・ラベル間隔を指定可能。({ hours: 4 }:4時間)
  • slotLabelFormat・・・ラベルフォーマットを指定可能。「hour」「minute」は時間・分。「numeric」は数字、「2-digit」はゼロ埋め。「omitZeroMinute」は0分を省略可能。「meridiem」は「午前」「午後表記」。「short」は「am」「pm」、「narrow」は「a」「p」、「false」はなし。
  • slotLabelContent・・・スロットラベル描画時のフック。スロット情報のオブジェクトを引数にとり、表示する文字列を返す。

上記例は曜日によって受付時間を変更する例です。

  • businessHours・・・曜日ごとの時間を設定可能。「daysOfWeek」で曜日を指定可能。(日曜:0 土曜:6)「startTime」で開始時間を指定可能。「endTime」で終了時間を指定可能。

事例

モーダルを使用してイベントを登録する

上記の例ではカレンダーをクリック、範囲選択を行うとモーダルが表示されます。そのモーダルの保存ボタンを押すとイベントがカレンダーに反映されるようになっています。

実際使用するとなるとモーダルではなくてもこのような使い方が多いのではないかと思います。

  • eventDisplay・・・カレンダーに表示される形を指定可能。「auto(default)」「block(色塗り)」「list-item(リスト形式)」「background(背景色)」「inverse-background(反転背景色)」「none(なし)」
  • displayEventTime・・・イベントの先頭に表示されるイベントの時間表記を表示するかどうか。
  • selectable・・・複数選択可能かどうか。
  • select・・・イベントが選択された際のコールバック。引数には開始日付や終了日付などが含まれる。
  • eventClick・・・登録されたイベントがクリックされた際のコールバック。引数にはイベントデータなどが含まれる。
セレクト・イベントクリック

(initEditModal)モーダルを表示しています。(表示位置の調整などの関数は読み飛ばして問題ありません。)

(setupModalData)セレクト時は開始日をイベントクリック時はイベントデータをモーダルの初期値として設定しています。

保存

新規追加時

「Callendar」インスタンスの「addEvent」でイベントを追加しています。

calendar.addEvent( {
start: start.value,
end: endDate,
title: title.value,
backgroundColor: color.value
} );

変更時

「eventClick」引数のオブジェクトに「event」があります。

これは「Event」のインスタンスなので、「setStart」「setEnd」「setProp」を利用してイベントの各設定を変更できます。

arg.event.setStart( start.value );
arg.event.setEnd( endDate );
arg.event.setProp( 'title', title.value );
arg.event.setProp( 'backgroundColor', color.value );
削除

「Event」インスタンスの「remove」でイベントを削除できます。

arg.event.remove();
モーダル処理終了

「保存」「キャンセル」「削除」のどれかを終えるとモーダルを閉じます。その際にカレンダーの選択が残ったままになるので、未選択状態へと変更します。

「Callendar」インスタンスの「unselect」を使用します。

calendar.unselect();

イベントに独自データを持たせたい

タイトル、時間など「Event」がもともと持っているもの以外に独自のデータを管理したい場合があると思います。

そのような場合のために「Event」には「extendedProps」というものがあります。

イベントを追加する場合

calendar.addEvent( {
extendedProps: {
userId: document.querySelector( '[data-user-id]' ).getAttribute( 'data-user-id' ),
}
} );

データベース連携する場合

return json_encode( [
[
'start' => $start,
'end' => $end,
'title' => $title,
'extendedProps' => [
'userId' => $user_id
]
],
] );

利用する場合

event.extendedProps.userId

データベース連携

こちらも実際の使用ケースとして多いと思います。

データベースの内容を表示する、カレンダーの内容を保存するという部分です。

データベースの内容を表示する

events: {
url: '/schedule.php'
}

カレンダーのオプション「events」にデータベースの内容を返すURLを指定します。

この設定を行うとカレンダーの初回描画時やビューの切り替え時、月・週・日の変更時などに自動的にアクセスして取得をします。

現在表示されているカレンダーに合わせて設定したURLに「start」「end」のクエリが付与されます。

/schedule.php?start=2022-07-31T00%3A00%3A00%2B09%3A00&end=2022-09-11T00%3A00%3A00%2B09%3A00

指定したURLではこのクエリを解析し、データベースからイベントデータを取り出し、jsonを返します。

return json_encode( [
[
'start' => $start,
'end' => $end,
'title' => $title,
],
...
] );

実際のケースだと「start」「end」だけでなく他の要素が欲しい場合があると思います。

その際には「extraParams」を設定することで任意のパラメーターを付与することができます。

events: {
url: '/schedule.php',
// 固定
extraParams: {
user_id: user_id
},
// 動的
extraParams: () => {
return {
user_id: document.querySelector( '[data-user-id]' ).getAttribute( 'data-user-id' ),
}
}
}
/schedule.php?start=2022-07-31T00%3A00%3A00%2B09%3A00&end=2022-09-11T00%3A00%3A00%2B09%3A00&user_id=0123456789

カレンダーの内容を保存する

イベントの取得はカレンダーに便利な関数がありますが、カレンダーの内容を保存する関数はありません。自分で用意する必要があります。

手順としては下記の2つです。

  • カレンダー内のイベントを取得する。
  • 取得したイベントを送信する。

カレンダーのイベントを取得するには「Calendar」の「getEvents」で可能です。「Event」の配列を取得できます。

取得したイベントから必要なデータを抜きだし整形し、ajaxなど利用してバックエンドに投げます。

// 例
const sendCalendarEvent = () => {
const events = calendar.getEvents();
const fetchBody = events.map( event => {
return {
start: event.startStr,
end: event.endStr,
title: event.title
};
} );
const data = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify( fetchBody )
};
fetch( url, data )
}
注意点

データベースから取得したデータに変更を加えた場合、ビューの切り替えなどによりデータの再取得が発生すると変更した内容が破棄されます。下記対応が必要になる場合があります。

  • 変更があった場合ビューの切り替えを行わせない。
  • 変更したイベントを変数に入れておく。
  • 変更後即時保存を行う。

所感

実際使用してかなりいろいろなカスタマイズが出来るようになっているなと感じました。作成している際に「ここのコールバックは」「ここ変更できないかな」みたいなのはすべて用意されていたぐらいです。

唯一面倒なのはデータベースと上手く連携させるという所ですね。保存ボタンで保存処理を行うのであれば、変更を加えたイベントや削除したイベントを変数に持っておいてビュー切り替え後の保存処理に対応しなければいけません。

有料の機能だとこの辺りは楽になるのかな?

2020 KumaTechLab.