fullcalendar V5 でデーターベースを絡めたサイトを構築

#試しにグーグルライクなスケジュールソフトを作ってみました。
週末、調べて作ってみました
ざっくり以下の環境が必要です。
1 fullcalendar V5 
2 node.js 6.14.5
3 Express 4.17.1(6.14.5で動くもの）
4 sqlite3 5.0(6.14.5で動くもの）

#ざっくり表のHTMLソースです
以下のようになっています。
これがV5の情報がなくて英語のマニュアルを読んで
作っていました。







```
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<link href='../lib/main.css' rel='stylesheet' />
<script src='../lib/main.js'></script>
<script src='../lib/locales/ja.js'></script>
<script>


    //alert(data);

  document.addEventListener('DOMContentLoaded', function() {
    var calendarEl = document.getElementById('calendar');

      var calendar = new FullCalendar.Calendar(calendarEl, {
          schedulerLicenseKey: 'GPL-My-Project-Is-Open-Source',
      now: '2020-06-07',
      locale:'ja',
      scrollTime: '00:00', // undo default 6am scrollTime
      editable: true, // enable draggable events
      selectable: true,
      aspectRatio: 1.8,
      headerToolbar: {
        left: 'today prev,next',
        center: 'title',
        right: 'resourceTimelineDay,resourceTimelineThreeDays,timeGridWeek,dayGridMonth,listWeek'
      },
      initialView: 'resourceTimelineDay',
      views: {
        resourceTimelineThreeDays: {
          type: 'resourceTimeline',
          duration: { days: 3 },
          buttonText: '3 days'
        }
      },
      resourceAreaHeaderContent: '営業先',
      resources: [
        { id: 'a', title: '営業先A' },
        { id: 'b', title: '営業先 B', eventColor: 'green' },
        { id: 'c', title: '営業先 C', eventColor: 'orange' },
        { id: 'd', title: '営業先 D', children: [
          { id: 'd1', title: '子 営業先 D1' },
          { id: 'd2', title: '子 営業先 D2' }
        ] },
        { id: 'e', title: '営業先 E' },
        { id: 'f', title: '営業先 F', eventColor: 'red' },
        { id: 'g', title: '営業先 G' },
        { id: 'h', title: '営業先 H' ,eventColor: 'orange' },
        { id: 'i', title: '営業先 I' },
        { id: 'j', title: '営業先 J', eventColor: 'green' },
        { id: 'k', title: '営業先 K' },
        { id: 'l', title: '営業先 L' },
        { id: 'm', title: '営業先 M' },
        { id: 'n', title: '営業先 N' },
        { id: 'o', title: '営業先 O' },
        { id: 'p', title: '営業先 P' },
        { id: 'q', title: '営業先 Q' },
        { id: 'r', title: '営業先 R' },
        { id: 's', title: '営業先 S' },
        { id: 't', title: '営業先 T' },
        { id: 'u', title: '営業先 U' },
        { id: 'v', title: '営業先 V' },
        { id: 'w', title: '営業先 W' },
        { id: 'x', title: '営業先 X' },
        { id: 'y', title: '営業先 Y' },
        { id: 'z', title: '営業先 Z' }
          ],
          eventResize: function (info) {
              //alert(info.event.id + " was dropped on " + info.event.start.toISOString());

              //if (!confirm("Are you sure about this change?")) {
              //    info.revert();
              //} else {




              //}



              //////////////////////////////////


              var event = calendar.getEventById(info.event.id);
              var resources = event.getResources();
              var resourceIds = resources.map(function (resource) { return resource.id });

              ////////////////////


              var request = new XMLHttpRequest();

              var req = 'http://157.7.138.66:3000/api/updata?resourceId=' + resourceIds + '&start=' + info.event.start.toISOString() + '&end=' + info.event.end.toISOString() + '&id=' + info.event.id

              request.open('GET', req, true);
              request.responseType = 'json';

              request.onload = function () {
                  var data = this.response;
                  console.log(data);

                  //alert(data);


                  calendar.addEventSource(data);


                  calendar.refetchEvents();

                  calendar.render();
              };


              request.send();

          },

      
          eventDrop: function (info) {
                  //alert(info.event.id + " was dropped on " + info.event.start.toISOString());

                  //if (!confirm("Are you sure about this change?")) {
                  //    info.revert();
                  //} else {

                    


                  //}



                  //////////////////////////////////


                  var event = calendar.getEventById(info.event.id);
                  var resources = event.getResources();
                  var resourceIds = resources.map(function (resource) { return resource.id });

                  ////////////////////


                  var request = new XMLHttpRequest();

                  var req = 'http://157.7.138.66:3000/api/updata?resourceId=' + resourceIds + '&start=' + info.event.start.toISOString() + '&end=' + info.event.end.toISOString() + '&id=' + info.event.id

                  request.open('GET', req, true);
                  request.responseType = 'json';

                  request.onload = function () {
                      var data = this.response;
                      console.log(data);

                      //alert(data);


                      calendar.addEventSource(data);


                      calendar.refetchEvents();

                      calendar.render();
                  };


                  request.send();

        },

        dateClick: function(info) {
        //日付をクリックしたときの処理
            //addEvent(calendar,info);
            //あとで使う関数
             alert(info);
        },
        
        
        
        
        	select:function(event, jsEvent){
                var title = prompt('予定を入力してください:', event.title);
                if (title && title != "") {
                    alert("[" + title + "] 作成しました");
                }
                alert("開発未定");

			//if(title && title!=""){
			//	event.title = title;
			//	calendar.fullCalendar('updateEvent', event); //イベント（予定）の修正
			//}else{
			//	calendar.fullCalendar("removeEvents", event.id); //イベント（予定）の削除				
			//}
	}
	
	
        
    });


      var request = new XMLHttpRequest();

      request.open('GET', 'http://157.7.138.66:3000/api/photo/list', true);
      request.responseType = 'json';

      request.onload = function () {
          var data = this.response;
          console.log(data);

          //alert(data);


          calendar.addEventSource(data);


          calendar.refetchEvents();

          calendar.render();
      };


      request.send();

    calendar.render();
  });








</script>
<style>

  body {
    margin: 0;
    padding: 0;
    font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
    font-size: 14px;
  }

  #calendar {
    max-width: 1100px;
    margin: 50px auto;
  }

</style>
</head>
<body>

  <div id='calendar'></div>

</body>
</html>
```

#DB側を少し解説

基本的にDBは更新と参照のみ。フレームワークはexpressを使っています。

```
//データーベースファイル　SQLITE3
var dbpath = "data.db";

//AJAXから受け取る情報をJSONで返す（ロード時の参照）
app.get("/api/photo/list", function (req, res, next) {
    db.all('SELECT id,resourceId,start,end,title from event', function (err, rows) {
        if (err) {
            throw err;
        }
        rows.forEach(function (row) {

            //console.log(row);
            data.push(row);
            console.log(data);

        });
        res.json(data);
        data = [];
    });
});


//AJAXから受け取る情報をSQLアップデートで更新する

app.get("/api/updata", function (req, res, next) {

    var sql = 'UPDATE event SET resourceId ="' + req.query.resourceId + '", start ="' + req.query.start + '" , end = "' + req.query.end +'" WHERE id = ' + req.query.id;

    db.run(sql, data, function (err) {
        if (err) {
            return console.error(err.message);
        }
    });
});


```

#だいたいはソースにある通りです。

裏のロジックは後日　執筆します。それよりもソースが欲しい方は以下で連絡してもらえば
メールと一緒に解説します。※2020/08/20 裏のロジックを執筆しました。

#サンプルサイト
http://157.7.138.66:3000/index2

サンプルサイトです。
ソースプログラムが欲しい方は
以下のメールアドレスからどうぞ連絡をください。

qqmdはちsrきゅうk@docomonet.jp 

平仮名は数値でお願いします（スパム除け）

*サンプルサイトは期間限定で公開しています。
*仮IDはusername
*仮PWはpasswordです