122
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

FullCalendarの使い方

FullCalendarの入手

FullCalendarに興味があったので触ってみました。

Googleで引っかかる情報はバージョンが古いようで最新版(4.3.1)で検証してみます。
FullCalendar-4.3.1

無題.png

とりあえず「Get Started」からzip版をダウンロードしてスタート

無題2.png

examplesの中にはいろんなタイプのサンプルがあるので
自分好みのベースをチョイスすればよいかな。

無題3.png

FullCarendarのサンプル

  • background-events.html
    background-events.png

  • daygrid-views.html
    daygrid-views.png

  • external-dragging-2cals.html
    external-dragging-2cals.png

  • external-dragging-builtin.html
    external-dragging-builtin.png

  • full-height.html
    full-height.png

  • google-calendar.html
    google-calendar.png

  • list-views.html
    list-views.png

  • locales.html
    locales.png

  • month-view.html
    month-view.png

  • rrule.html
    rrule.png

  • selectable.html
    selectable.png

  • theming.html
    theming.png

  • time-zones.html
    time-zones.png

  • timegrid-views.html
    timegrid-views.png

  • week-numbers.html
    week-numbers.png

好みをチョイス

わたしの好みはベース的には「background-events.html」
追加機能として「locales.html」で日本語化したものが最適かな(^^♪

background-events.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>
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/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を日本語化したい。

日付、曜日、時間を日本語化

mycalendar.html
    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: [
        {

これを

mycalendar.html
    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: [
        {

とすると、これが
background-events.png

こうなりました。
mycalendar01.png

上部のメニューの日本語化

どうせなら上部のtoday、month、week、day、listも日本語化したいですよね。
本格的なlocalesのjsを追加します。

mycalendar.html
<script src="../packages/core/locales-all.js"></script>

mycalendar02.png

土日の色付け

因みに土日の色付けはbusinessHours:でコントロールされています。
土日の色付けを消したい場合はbusinessHours: false,で消えるようです。

消してみた(^^♪

mycalendar03.png

月をずらすボタン<>を変えてみる

先月、来月に移動するボタンはデフォルトで<>となっていますが
これを日本語化もできます。

buttonIcons:でコントロールされているのでbuttonIcons: falseとします。

mycalendar.html
      locale: 'ja',
      defaultDate: '2019-08-12',

設定してみるとこんな感じ

mycalendar.html
      locale: 'ja',
      buttonIcons: false, // show the prev/next text
      defaultDate: '2019-08-12',

mycalendar04.png

うん。好みが判れそう(笑)
わたしは<>でいいかな。

timezoneの設定

docs::timeZone

timezoneの設定はtimeZone:で指定します。
timeZone: 'Asia/Tokyo'
これによりeventsの時間表記が
 2018-09-01T12:30:00+09:00
といった形式から
 2018-09-01T12:30:00
だけにできます。

まあtimeZone: 'locale'がデフォルトなので日本圏でのみのサイトなら指定しなくても良いでしょうが…

mycalendar.html
    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を指定します。

mycalendar.html
        {
          start: '2019-08-06',
          end: '2019-08-08',
          overlap: false,
          rendering: 'background',
          color: '#ff9f89'
        }
      ],
      eventTimeFormat: { hour: 'numeric', minute: '2-digit' }

これが
mycalendar01.png

こうなります。ちょっとスッキリするかも

mycalendar05.png

デフォルト表示の切り替え

デフォルトのViewの切り替えは文字通りdefaultViewにて行います。
例えば管理画面などはカレンダー表示よりリスト表示の方が良いこともありますよね。
そんな時はこんな感じで

mycalendar.html
      defaultView: 'listMonth',

eventsの書き方

通常イベント(時間枠)

Partyの方は通常イベント。デフォルトではendを指定しないと1時間枠となります。

events
        {
          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を指定しないとその日一日の終日イベントとなります。

events
        {
          title: 'Conference',
          start: '2019-08-18',
          end: '2019-08-20'
        },
        {
          title: 'Party(1day)',
          start: '2019-08-28T20:00:00'
        },

受付できない期間

overlap: falseでそこに予定がドロップできなくなります。
また、禁止期間が判りやすいようにcolorを指定していますね。

events
        {
          start: '2019-08-06',
          end: '2019-08-08',
          overlap: false,
          rendering: 'background',
          color: '#ff9f89'
        }

特定イベントのみ登録できる期間

constraint: 'xxx'を指定した場合は、groupId: 'xxx'で指定した期間にのみ登録できるようです。

events
        {
          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とする必要があります。

mycalendar.html
      businessHours: true, // display business hours
events
        {
          title: 'Business Lunch',
          start: '2019-08-03T13:00:00',
          constraint: 'businessHours'
        },

businessHourを定義する

Docs :: Business Hours

出社時間や曜日を指定する際は以下のようにします。

mycalendar.html
      businessHours: true, // display business hours

我が社は月~金 9:30-18:00ですのでこんな感じ。

mycalendar.html
      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
mycalendar.html
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に取得すべき日時範囲が出力されます。

mycalendar.html
  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をクリックして業務アプリと連携したい

Docs : Event Object

eventをクリックした際に業務アプリケーション側にそのイベントを渡したいって事ちょいちょいありますよね。
そんな時はこんな感じで実装すると良さそう。

まずeventsにはeventを一意に特定するキー情報を付与します。(id: UUIDとか)
このidというアトリビュートはカレンダー標準でもっているアトリビュートです。
カレンダーはeventに独自のパラメータを拡張し保持することもできます。
例えばステータスといったものを追加します。(status)
するとカレンダーに登録する際のJSONはこんな感じ。

events
        {
          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情報をクリックしたときに受け取りたい場合、こんな感じ。

mycalendar.html
      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オブジェクトのプロパティはこちらからどうそ

Docs : event object

mycalendar.html
<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の定義をコメントアウトすればちゃんとカレンダー表示通りの時間が返ってきます。

mycalendar.html
<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>

まだまだ

まだまだ色々ありそうなので、必要そうで分かり次第アップデートしていきます。

最後に

他にも一杯オプションがあります。

FullCalendar :: doc

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
122
Help us understand the problem. What are the problem?