FullCalendarの入手
FullCalendarに興味があったので触ってみました。
Googleで引っかかる情報はバージョンが古いようで最新版(4.3.1)で検証してみます。
FullCalendar-4.3.1
とりあえず「Get Started」からzip版をダウンロードしてスタート
examplesの中にはいろんなタイプのサンプルがあるので
自分好みのベースをチョイスすればよいかな。
FullCarendarのサンプル
好みをチョイス
わたしの好みはベース的には「background-events.html」
追加機能として「locales.html」で日本語化したものが最適かな(^^♪
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<link href="../packages/core/main.css" rel="stylesheet" />
<link href="../packages/daygrid/main.css" rel="stylesheet" />
<link href="../packages/timegrid/main.css" rel="stylesheet" />
<link href="../packages/list/main.css" rel="stylesheet" />
<script src="../packages/core/main.js"></script>
<script src="../packages/interaction/main.js"></script>
<script src="../packages/daygrid/main.js"></script>
<script src="../packages/timegrid/main.js"></script>
<script src="../packages/list/main.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
plugins: [ 'interaction', 'dayGrid', 'timeGrid', 'list' ],
header: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay,listMonth'
},
defaultDate: '2019-08-12',
navLinks: true, // can click day/week names to navigate views
businessHours: true, // display business hours
editable: true,
events: [
{
title: 'Business Lunch',
start: '2019-08-03T13:00:00',
constraint: 'businessHours'
},
{
title: 'Meeting',
start: '2019-08-13T11:00:00',
constraint: 'availableForMeeting', // defined below
color: '#257e4a'
},
{
title: 'Conference',
start: '2019-08-18',
end: '2019-08-20'
},
{
title: 'Party',
start: '2019-08-29T20:00:00'
},
// areas where "Meeting" must be dropped
{
groupId: 'availableForMeeting',
start: '2019-08-11T10:00:00',
end: '2019-08-11T16:00:00',
rendering: 'background'
},
{
groupId: 'availableForMeeting',
start: '2019-08-13T10:00:00',
end: '2019-08-13T16:00:00',
rendering: 'background'
},
// red areas where no events can be dropped
{
start: '2019-08-24',
end: '2019-08-28',
overlap: false,
rendering: 'background',
color: '#ff9f89'
},
{
start: '2019-08-06',
end: '2019-08-08',
overlap: false,
rendering: 'background',
color: '#ff9f89'
}
]
});
calendar.render();
});
</script>
<style>
body {
margin: 40px 10px;
padding: 0;
font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
font-size: 14px;
}
#calendar {
max-width: 900px;
margin: 0 auto;
}
</style>
</head>
<body>
<div id='calendar'></div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<link href="../packages/core/main.css" rel="stylesheet" />
<link href="../packages/daygrid/main.css" rel="stylesheet" />
<link href="../packages/timegrid/main.css" rel="stylesheet" />
<link href="../packages/list/main.css" rel="stylesheet" />
<script src="../packages/core/main.js"></script>
<script src="../packages/core/locales-all.js"></script>
<script src="../packages/interaction/main.js"></script>
<script src="../packages/daygrid/main.js"></script>
<script src="../packages/timegrid/main.js"></script>
<script src="../packages/list/main.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
var initialLocaleCode = 'en';
var localeSelectorEl = document.getElementById('locale-selector');
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
plugins: [ 'interaction', 'dayGrid', 'timeGrid', 'list' ],
header: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay,listMonth'
},
defaultDate: '2019-08-12',
locale: initialLocaleCode,
buttonIcons: false, // show the prev/next text
weekNumbers: true,
navLinks: true, // can click day/week names to navigate views
editable: true,
eventLimit: true, // allow "more" link when too many events
events: [
{
title: 'All Day Event',
start: '2019-08-01'
},
{
title: 'Long Event',
start: '2019-08-07',
end: '2019-08-10'
},
{
groupId: 999,
title: 'Repeating Event',
start: '2019-08-09T16:00:00'
},
{
groupId: 999,
title: 'Repeating Event',
start: '2019-08-16T16:00:00'
},
{
title: 'Conference',
start: '2019-08-11',
end: '2019-08-13'
},
{
title: 'Meeting',
start: '2019-08-12T10:30:00',
end: '2019-08-12T12:30:00'
},
{
title: 'Lunch',
start: '2019-08-12T12:00:00'
},
{
title: 'Meeting',
start: '2019-08-12T14:30:00'
},
{
title: 'Happy Hour',
start: '2019-08-12T17:30:00'
},
{
title: 'Dinner',
start: '2019-08-12T20:00:00'
},
{
title: 'Birthday Party',
start: '2019-08-13T07:00:00'
},
{
title: 'Click for Google',
url: 'http://google.com/',
start: '2019-08-28'
}
]
});
calendar.render();
// build the locale selector's options
calendar.getAvailableLocaleCodes().forEach(function(localeCode) {
var optionEl = document.createElement('option');
optionEl.value = localeCode;
optionEl.selected = localeCode == initialLocaleCode;
optionEl.innerText = localeCode;
localeSelectorEl.appendChild(optionEl);
});
// when the selected option changes, dynamically change the calendar option
localeSelectorEl.addEventListener('change', function() {
if (this.value) {
calendar.setOption('locale', this.value);
}
});
});
</script>
<style>
body {
margin: 0;
padding: 0;
font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
font-size: 14px;
}
#top {
background: #eee;
border-bottom: 1px solid #ddd;
padding: 0 10px;
line-height: 40px;
font-size: 12px;
}
#calendar {
max-width: 900px;
margin: 40px auto;
padding: 0 10px;
}
</style>
</head>
<body>
<div id='top'>
Locales:
<select id='locale-selector'></select>
</div>
<div id='calendar'></div>
</body>
</html>
なるほどlocale: initialLocaleCode,
だからlocale: 'ja',
で良さそうですね。
FullCarendarを日本語化したい。
日付、曜日、時間を日本語化
var calendar = new FullCalendar.Calendar(calendarEl, {
plugins: [ 'interaction', 'dayGrid', 'timeGrid', 'list' ],
header: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay,listMonth'
},
defaultDate: '2019-08-12',
navLinks: true, // can click day/week names to navigate views
businessHours: true, // display business hours
editable: true,
events: [
{
これを
var calendar = new FullCalendar.Calendar(calendarEl, {
plugins: [ 'interaction', 'dayGrid', 'timeGrid', 'list' ],
header: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay,listMonth'
},
locale: 'ja',
defaultDate: '2019-08-12',
navLinks: true, // can click day/week names to navigate views
businessHours: true, // display business hours
editable: true,
events: [
{
上部のメニューの日本語化
どうせなら上部のtoday、month、week、day、listも日本語化したいですよね。
本格的なlocalesのjsを追加します。
<script src="../packages/core/locales-all.js"></script>
土日の色付け
因みに土日の色付けはbusinessHours:
でコントロールされています。
土日の色付けを消したい場合はbusinessHours: false,
で消えるようです。
消してみた(^^♪
月をずらすボタン<>を変えてみる
先月、来月に移動するボタンはデフォルトで<>となっていますが
これを日本語化もできます。
buttonIcons:
でコントロールされているのでbuttonIcons: false
とします。
locale: 'ja',
defaultDate: '2019-08-12',
設定してみるとこんな感じ
locale: 'ja',
buttonIcons: false, // show the prev/next text
defaultDate: '2019-08-12',
うん。好みが判れそう(笑)
わたしは<>でいいかな。
timezoneの設定
timezoneの設定はtimeZone:
で指定します。
timeZone: 'Asia/Tokyo'
これによりevents
の時間表記が
2018-09-01T12:30:00+09:00
といった形式から
2018-09-01T12:30:00
だけにできます。
まあtimeZone: 'locale'
がデフォルトなので日本圏でのみのサイトなら指定しなくても良いでしょうが…
var calendar = new FullCalendar.Calendar(calendarEl, {
plugins: [ 'interaction', 'dayGrid', 'timeGrid', 'list' ],
header: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay,listMonth'
},
timeZone: 'Asia/Tokyo',
locale: 'ja',
defaultDate: '2019-08-12',
navLinks: true, // can click day/week names to navigate views
businessHours: true, // display business hours
editable: true,
events: [
{
カレンダーの表示時間はhh:mm形式にしたい
日本語化するとカレンダー内のイベント時刻も「xx時」と表示されます。
これをhh:mm形式にしたい場合は以下の様に修正します。
場所は何処でも良さそうですがeventTimeFormat
を指定します。
{
start: '2019-08-06',
end: '2019-08-08',
overlap: false,
rendering: 'background',
color: '#ff9f89'
}
],
eventTimeFormat: { hour: 'numeric', minute: '2-digit' }
こうなります。ちょっとスッキリするかも
デフォルト表示の切り替え
デフォルトのViewの切り替えは文字通りdefaultView
にて行います。
例えば管理画面などはカレンダー表示よりリスト表示の方が良いこともありますよね。
そんな時はこんな感じで
defaultView: 'listMonth',
eventsの書き方
通常イベント(時間枠)
Party
の方は通常イベント。デフォルトではend
を指定しないと1時間枠となります。
{
title: 'Party(1h)',
start: '2019-08-29T20:00:00'
},
{
title: 'Party(1.5h)',
start: '2019-08-29T18:00:00',
end: '2019-08-29T19:30:00'
},
通常イベント(終日枠)
Conference
の方は複数日をまたがるイベント。終了日のend
を指定しないとその日一日の終日イベントとなります。
{
title: 'Conference',
start: '2019-08-18',
end: '2019-08-20'
},
{
title: 'Party(1day)',
start: '2019-08-28T20:00:00'
},
受付できない期間
overlap: false
でそこに予定がドロップできなくなります。
また、禁止期間が判りやすいようにcolorを指定していますね。
{
start: '2019-08-06',
end: '2019-08-08',
overlap: false,
rendering: 'background',
color: '#ff9f89'
}
特定イベントのみ登録できる期間
constraint: 'xxx'
を指定した場合は、groupId: 'xxx'
で指定した期間にのみ登録できるようです。
{
title: 'Meeting',
start: '2019-08-13T11:00:00',
constraint: 'availableForMeeting', // defined below
color: '#257e4a'
},
// areas where "Meeting" must be dropped
{
groupId: 'availableForMeeting',
start: '2019-08-11T10:00:00',
end: '2019-08-11T16:00:00',
rendering: 'background'
},
{
groupId: 'availableForMeeting',
start: '2019-08-13T10:00:00',
end: '2019-08-13T16:00:00',
rendering: 'background'
},
特定イベントのみ登録できる期間(平日日勤帯)
constraint: 'businessHours'
とした場合、月~金の9:00-17:00までがデフォルトで登録可能のようです。
ただし、カレンダーオプションでbusinessHour: true
とする必要があります。
businessHours: true, // display business hours
{
title: 'Business Lunch',
start: '2019-08-03T13:00:00',
constraint: 'businessHours'
},
businessHour
を定義する
出社時間や曜日を指定する際は以下のようにします。
businessHours: true, // display business hours
我が社は月~金 9:30-18:00ですのでこんな感じ。
businessHours: {
// days of week. an array of zero-based day of week integers (0=Sunday)
daysOfWeek: [ 1, 2, 3, 4, 5 ],
startTime: '9:30',
endTime: '18:00'
},
イベント
カレンダーが表示されたらデータを取ってきたい
カレンダーは基本1か月分しか表示しないので「<」「>」や「今日」を押したときにデータを持ってきたいですよね。
どうやら標準的な機能で網羅されているようでした。
以下のようにevents
を動的にjsonを出力するphp等を指定するとGETでパラメータ付きで自動的にCallしてくれます。
下記のようにした場合、./getEvents.phpは次のように呼ばれます。
./getEvents.php?start=2019-12-29T00%3A00%3A00&end=2020-02-09T00%3A00%3A00&timeZone=Asia%2FTokyo
param | value |
---|---|
start | 2019-12-29T00:00:00 |
end | 2020-02-09T00:00:00 |
TimeZone | Asia/Tokyo |
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
plugins: [ 'dayGrid', 'timeGrid', 'bootstrap' ],
locale: 'ja',
timeZone: 'Asia/Tokyo',
themeSystem: 'bootstrap',
businessHours: true, // display business hours
header: {
left: 'prev,next today',
center: 'sMeeting説明会',
right: 'dayGridMonth,timeGridWeek,timeGridDay,listMonth'
},
weekNumbers: false,
navLinks: true, // can click day/week names to navigate views
editable: false,
eventLimit: true, // allow "more" link when too many events
events: './getEvents.php'
});
calendar.render();
});
そんな時はdatesRender
イベントを活用すると実現できそう。
このイベントViewが変わると呼ばれますのでグローバル変数で現在の開始終了日時を確保しておき、その範囲外の場合はeventsを取ってくるという具合です。
まだ勉強不足で動的なイベント生成からの描画は未実装ですが、カレンダーの「<」「>」や「今日」を押したときはconsole.logに取得すべき日時範囲が出力されます。
var dateStart = null;
var dateEnd = null;
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
plugins: [ 'interaction', 'dayGrid', 'timeGrid', 'list' ],
header: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay,listMonth'
},
locale: 'ja',
buttonIcons: false, // show the prev/next text
defaultDate: '2019-08-12',
navLinks: true, // can click day/week names to navigate views
businessHours: {
daysOfWeek: [ 1, 2, 3, 4, 5 ],
startTime: '9:30',
endTime: '18:00'
},
editable: true,
eventLimit: true, // allow "more" link when too many events
eventTimeFormat: { hour: 'numeric', minute: '2-digit' },
datesRender: function(info) {
var ds = info.view.activeStart.toJSON();
var de = info.view.activeEnd.toJSON();
if( ( dateStart == null || Date.parse(dateStart) > Date.parse(ds)) || ( dateEnd == null || Date.parse(dateEnd) < Date.parse(de) ) ){
dateStart = ds;
dateEnd = de;
console.log('render:'+ds+" - "+de);
}
}
});
calendar.render();
});
eventをクリックして業務アプリと連携したい
eventをクリックした際に業務アプリケーション側にそのイベントを渡したいって事ちょいちょいありますよね。
そんな時はこんな感じで実装すると良さそう。
まずeventsにはeventを一意に特定するキー情報を付与します。(id
: UUIDとか)
このid
というアトリビュートはカレンダー標準でもっているアトリビュートです。
カレンダーはeventに独自のパラメータを拡張し保持することもできます。
例えばステータスといったものを追加します。(status
)
するとカレンダーに登録する際のJSONはこんな感じ。
{
id: '262399c4-3d9c-4d4f-b090-c8d8e8b50358',
title: 'Business Lunch',
start: '2019-08-03T13:00:00',
status: 'finished'
},
{
id: '3a05c506-3e5a-4e33-bd9e-e2a92ae37e27',
title: 'Business Lunch',
start: '2019-08-03T13:00:00',
status: 'resered'
},
この登録したevent情報をクリックしたときに受け取りたい場合、こんな感じ。
eventRender: function(info) {
info.el.onclick=function(){
alert(info.event.id + ":" + info.event.extendedProps.status);
};
}
});
eventRenderでeventを描画する際にcallback関数を仕込みます。
上の例では...
eventは引数であるinfo.elで参照できるので、onclick時にcallback関数を仕込んでいます。
eventの情報はinfo.event.*
で参照できますので、カレンダー組み込みのアトリビュート
例えばid
ならinfo.event.id
で参照できます。
先ほど拡張したstatus
はというとinfo.event.extendedProps.*
で参照できます。
つまりinfo.event.extendedProps.status
ですね。
今回は単純にalertでeventの属性情報を表示するのみとしていますが
ajaxで情報取得し編集画面を表示するもよし、画面遷移で編集画面にするもよし
活用シーンは色々あると思います。
カレンダーに登録されているイベントを引っ張ってきたい
カレンダーオブジェクトに定義されているイベント情報を引っ張ってきてJQueryなどで利用したいシーンもあるかも
例えば予約フォームをモーダルで表示して日付、時間が変更されたのを契機に既存イベントと重複しないかチェックとか
そんな場合はこのようにします。
var events = calendar.getEvents();
for(var i=0; i<events.length; i++){
alert(events[i].title + "..." + events[i].start);
}
取れるenentオブジェクトのプロパティはこちらからどうそ
<script>
var calendar = null; // calendarオブジェクトをグローバル変数にしておく
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
calendar = new FullCalendar.Calendar(calendarEl, { // varをカットしグローバル変数を利用
plugins: [ 'interaction', 'dayGrid', 'timeGrid' ],
header: {
left: 'prev,next today',
:
</script>
:
<!-- html -->
:
<script>
$('#EntryDate').on('change',function(){
if( /^[0-9]{4}\/[0-9]{2}\/[0-9]{2}$/.test($(this).val()) ){
checkEntryDateTime( $(this).val(), $('#EntryTime').val() );
} else {
$('#errmsg').text('平日受付可能日を選択してください');
$('#entry_errmsg').addClass('show');
}
});
$('#EntryTime').on('change',function(){
if( /^[0-9]{4}\/[0-9]{2}\/[0-9]{2}$/.test($('#EntryDate').val()) ){
checkEntryDateTime( $('#EntryDate').val(), $(this).val() );
}
});
function checkEntry(d, t){ // d:YYYY/MM/DD, t:hh:mm
if( d && t ){
dd = new Date(d);
dt = new Date(d + " " + t + ":00");
// 予約重複確認
var events = calendar.getEvents(); // calendarオブジェクトからイベントを取得
for(var i=0; i<events.length; i++){
if( events[i].start == dt ){
$('#errmsg').text('受付が重複しています。受付可能日の空いている日時を選択してください');
$('#entry_errmsg').addClass('show');
} else if ( events[i].id == 'nonReception' && events[i].start == dd ){
$('#errmsg').text('受付可能日を選択してください');
$('#entry_errmsg').addClass('show');
}
}
}
}
</script>
実際に実験してみたのですが…どうもevents
に定義している日時と
getEvents()
で取ったイベントの日時の時間がずれてて使い物になりませんでした…。
calendarのeventsで定義した値
param | value |
---|---|
title | Business Lunch |
start | 2019-08-03T13:00:00 |
constraint | businessHours |
実際にeventsで取得した時の値
param | value |
---|---|
events[0].start | Sat Aug 03 2019 22:00:00 GMT+0900 |
events[0].title | Business Lunch |
見事に09:00分足されてますよね...
TimeZone
の定義がおかしいのかなぁ
時間ズレの原因判明
上記時間ズレの原因が判明しました♪
原因はlocale
で定義したうえで更にtimeZone
の定義をしていたためでした。
下記の通り、timeZone
の定義をコメントアウトすればちゃんとカレンダー表示通りの時間が返ってきます。
<script>
var calendar = null;
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
calendar = new FullCalendar.Calendar(calendarEl, {
plugins: [ 'interaction', 'dayGrid', 'timeGrid' ],
header: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
},
locale: 'ja',
// timeZone: 'Asia/Tokyo',
defaultDate: '2019-08-12',
navLinks: true, // can click day/week names to navigate views
businessHours: true, // display business hours
editable: true,
events: [
{
id: 'event',
title: 'Business Lunch',
start: '2019-08-03T13:00:00',
constraint: 'businessHours'
}
],
eventTimeFormat: { hour: 'numeric', minute: '2-digit' }
});
calendar.render();
});
</script>
まだまだ
まだまだ色々ありそうなので、必要そうで分かり次第アップデートしていきます。
最後に
他にも一杯オプションがあります。