Google Apps Scriptでフォームを作ってスプレッドシートに登録する方法

GoogleAppsScript
スポンサーリンク

今回の目的

今回はGoogle Apps Scriptを使って1からフォーム画面を作成し、入力内容をGoogleスプレッドシートに登録して管理する方法をご紹介します。

似ている方法でGoogleフォームの画面をカスタマイズする方法がありますが、そちらはこの記事でご紹介しています。

スプレッドシートをデータベース代わりに使うことでSQLの知識がなくても作成できることや、スプレッドシートからまとめて入力内容を見ることができるのでそういう点でメリットがあります。

作ったもの

フォーム

まずはフォーム画面

今回はBulmaというCSSフレームワークを使っているので、CSS自分でがっつり書かなくてよかったのとスマホで見ても崩れないというレスポンシブ対応ができました。

自由に作れることが伝わったらいいなと思います。

スプレッドシート

登録先のスプレッドシート

項目は何番目にどのパラメータを入れるかを指定するので、こちらも自由に作成できます。

フォームにない項目も用意できます。(今回だったら登録日)

作ろうと思ったきっかけ

きっかけとしては友人に「シェアオフィスを時間貸しするサービスを始めるので、利用者が使いたい時間を申請できるフォームを作ってほしい。」と言われたからです。

そういった背景からフォームを作ったのでフォームにある項目はそれに合わせて作っています。

作成手順

作成手順は

  1. GASでHTMLを表示できるようにする。
  2. HTMLでフォーム画面を作る。
  3. フォームを送信したときの処理をHTMLとGASで作る。
  4. GASでスプレッドシートに書きこむ処理を作る。

この手順でご説明していきます。

GASでHTMLを表示できるようにする。

こちらは定番の方法なのですぐにできると思います。

やり方は以前の記事でご紹介しています。

一応ソースを乗せておきます。

function doGet(e) {
  let page = 'index';
  const template = HtmlService.createTemplateFromFile(page);
  return template.evaluate();
}
function getAppUrl() {
  return ScriptApp.getService().getUrl();
}

getAppUrlは画面のURLを取得する関数です。

HTMLでフォーム画面を作る。

まずはHTMLで項目を配置していきます。

今回はCSSフレームワークのBulmaでUIを整えているので少し複雑になっていますが、ここはformタグとinputタグがあれば動かすことができます。

CSSは細かい調整をしているので今回は解説を割愛します。

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>シェアキッチン予約フォーム</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://use.fontawesome.com/releases/v5.3.1/js/all.js" defer></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.min.css" />
    <link rel="stylesheet"
        href="https://cdnjs.cloudflare.com/ajax/libs/jquery-timepicker/1.13.16/jquery.timepicker.min.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-timepicker/1.13.16/jquery.timepicker.min.js"></script>
</head>

<body class="is-flex is-justify-content-center has-background-light">
    <div class="main has-background-white is-flex-direction-column">
        <div class="conteiner form-title">
            <section class="hero is-info">
                <div class="hero-body">
                    <div class="container">
                        <h1 class="title">
                            シェアキッチン予約フォーム(β版)
                        </h1>
                    </div>
                </div>
            </section>
        </div>
        <div id="registerContent" class="conteiner form-body">
            <form id="reserveForm" action="<?= getAppUrl() ?>" method="post">
                <div class="field">
                    <label class="label" for="userId">ユーザID(登録メールアドレス)<span class="tag is-danger">必須</span></label>
                    <div class="control has-icons-left">
                        <input class="input" type="email" id="userId" name="user_id" placeholder="Email" />
                        <span class="icon is-small is-left">
                            <i class="fas fa-envelope"></i>
                        </span>
                    </div>
                    <p class="inquiry-text"></p>
                </div>
                <div class="field">
                    <label class="label" for="userName">氏名<span class="tag is-danger">必須</span></label>
                    <div class="control has-icons-left">
                        <input class="input" type="text" id="userName" name="user_name" placeholder="Name" />
                        <span class="icon is-small is-left">
                            <i class="fas fa-user"></i>
                        </span>
                    </div>
                </div>
                <div class="field calendar-field">
                    <label class="label" for="">開始日時<span class="tag is-danger">必須</span></label>
                    <div class="control calendar-control is-flex is-flex-wrap-nowrap">
                        <div><input class="input" type="date" id="calendarDateFrom" name="calendar_date_from" />
                            <input class="timepicker input" type="text" name="calendar_time_from" id="calendarTimeFrom"
                                data-time-format="H:i">
                        </div>
                        <label class="label" for="">終了日時<span class="tag is-danger">必須</span></label>
                        <div>
                            <input class="input" type="date" id="calendarDateTo" name="calendar_date_to" />
                            <input class="timepicker input" type="text" name="calendar_time_to" id="calendarTimeTo"
                                data-time-format="H:i">
                        </div>
                    </div>
                </div>
                <div class="field">
                    <label class="label" for="comment">備考</label>
                    <div class="control">
                        <textarea class="textarea " id="comment" name="comment" placeholder="Comment"></textarea>
                    </div>
                </div>
                <div class="field">
                    <div class="control">
                        <button type="button" id="submitButton" class="submit-button button is-info">確認画面</button>
                    </div>
                    <div class="modal" id="confirmModal">
                        <div class="modal-background"></div>
                        <div class="modal-card">
                            <header class="modal-card-head">
                                <p class="modal-card-title">確認</p>
                                <button type="button" class="delete cancel" aria-label="close"></button>
                            </header>
                            <section class="modal-card-body">
                                <table class="table is-striped is-fullwidth">
                                    <tbody>
                                        <tr>
                                            <th>ユーザID(登録メールアドレス)</th>
                                            <td id="userIdTd"></td>
                                        </tr>
                                        <tr>
                                            <th>氏名</th>
                                            <td id="userNameTd"></td>
                                        </tr>
                                        <tr>
                                            <th>開始日時</th>
                                            <td id="startDateTd"></td>
                                        </tr>
                                        <tr>
                                            <th>終了日時</th>
                                            <td id="endDateTd"></td>
                                        </tr>
                                        <tr>
                                            <th>備考</th>
                                            <td id="commentTd"></td>
                                        </tr>
                                    </tbody>
                                </table>
                            </section>
                            <footer class="modal-card-foot">
                                <button type="button" id="registerButton" class="button is-success">登録</button>
                                <button type="button" class="button cancel">キャンセル</button>
                            </footer>
                        </div>
                    </div>
                </div>
            </form>
        </div>
    </div>

    <script>
        //あとでjQueryを実装する。
    </script>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        body {
            width: 100vw;
            min-height: 100vh;
        }

        .main {
            margin: 20px;
            width: 60%;
            box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
            border-radius: 6px;
        }

        .form-body {
            padding: 20px;
        }

        #reserveForm {
            width: 100%;

        }

        .is-justify-content-center {
            justify-content: center;
        }

        .is-flex-direction-column {
            flex-direction: column;
        }

        .calendar-field input[type=date] {
            width: 160px;
            margin: 10px auto;
        }

        .calendar-field .timepicker {
            width: 100px;
            margin: 10px 10px 10px 0;
        }

        .form-title .hero-body {
            padding: 1.4rem 0.8rem;
        }

        .form-title .title {
            font-size: 1.2rem;
        }

        .form-body .field:not(:last-child) {
            margin-bottom: 1.5rem;
        }

        .is-flex-wrap-nowrap {
            flex-wrap: wrap;
        }

        .form-body #userId {
            width: 60%;
        }

        .form-body .submit-button {
            padding-left: 3em;
            padding-right: 3em;
        }

        .calendar-control>div {
            width: 100%;
        }

        /*レスポンシブ(スマホ)*/
        @media screen and (max-width: 480px) {
            body {
                min-height: 100%;
            }

            .main {
                width: 100%;
            }

            .form-body .form-text {
                display: none;
            }
        }
    </style>

</body>

</html>

今回読み込んでいるものを一応解説します。

    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>

おなじみjQueryです。これからjQuery書いていくための前準備。

    <script src="https://use.fontawesome.com/releases/v5.3.1/js/all.js" defer></script>

Font Awesome

いい感じのアイコンが使えるようになります。結構種類があってつかいやすい。

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.min.css" />

さっきから何回か出てきているBulma。

他の英語のCSSフレームワークに比べてドキュメントから理解しやすい。

<link rel="stylesheet"
        href="https://cdnjs.cloudflare.com/ajax/libs/jquery-timepicker/1.13.16/jquery.timepicker.min.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-timepicker/1.13.16/jquery.timepicker.min.js"></script>

jQuery timepicker

jQuery関連のプラグインで時間の入力がしやすくなります。

フォームを送信したときの処理をHTMLとGASで作る。

先ほど作成したHTMLでjQueryを使ってフォームをサブミットする処理とgas側で処理に使う関数を作ります。

まずはHTML側のソースです。先ほど作ったindex.htmlの<scirpt></script>に追加してください。

    <script>
    $(function () {
        $('.timepicker').timepicker();
        //確認画面ボタンクリック時処理
        $('#submitButton').on('click', function () {
            const userId = $('#userId').val();
            const userName = $('#userName').val();
            const calendarDateFrom = $('#calendarDateFrom').val();
            const calendarTimeFrom = $('#calendarTimeFrom').val();
            const calendarDateTo = $('#calendarDateTo').val();
            const calendarTimeTo = $('#calendarTimeTo').val();
            const now = new Date();
            const fullStartDate =  new Date(calendarDateFrom + ' '+ calendarTimeFrom+ ':00');
            const fullEndDate =  new Date(calendarDateTo + ' '+ calendarTimeTo+ ':00');
            $('#userIdTd').text(userId);
            $('#userNameTd').text(userName);
            $('#startDateTd').text(calendarDateFrom + ' ' + calendarTimeFrom);
            $('#endDateTd').text(calendarDateTo + ' ' + calendarTimeTo);
            $('#commentTd').html($('#comment').val().replace(/\r?\n/g, '<br />'));
            $('#confirmModal').show();
        });
        //確認モーダルのキャンセルボタンクリック時処理
        $('#confirmModal .cancel').on('click', function () {
            $('#confirmModal').hide();
        });
        //確認モーダルの登録ボタンクリック時処理
        $('#registerButton').on('click', function () {
            $('.regist-loading-wrapper').show();
            const req = {};
            const params = {};
            $('#reserveForm').find('input').each(function (index, element) {
                const key = $(element).attr('name');
                const val = $(element).val();
                params[key] = val;
            });
            req.parameters = params;
            google.script.run.withSuccessHandler(doSubmitSuccess).doSubmitAjax(req);
        });
    });
    const doSubmitSuccess = function (result) {
        //必要な場合、成功したときの画面処理を書く
    };
</script>

また処理を行うコード.gsも追加します。


function doSubmitAjax(req) {
    const params = req.parameters;
    const resObj = {};
    return resObj;
  }

順番に解説していきます。

$('.timepicker').timepicker();

これは時間を入力する項目にjQuery timepickerを適用させています。時間の入力する項目を使わない場合は必要ないです。

        //確認画面ボタンクリック時処理
        $('#submitButton').on('click', function () {
            const userId = $('#userId').val();
            const userName = $('#userName').val();
            const calendarDateFrom = $('#calendarDateFrom').val();
            const calendarTimeFrom = $('#calendarTimeFrom').val();
            const calendarDateTo = $('#calendarDateTo').val();
            const calendarTimeTo = $('#calendarTimeTo').val();
            const now = new Date();
            const fullStartDate =  new Date(calendarDateFrom + ' '+ calendarTimeFrom+ ':00');
            const fullEndDate =  new Date(calendarDateTo + ' '+ calendarTimeTo+ ':00');
            $('#userIdTd').text(userId);
            $('#userNameTd').text(userName);
            $('#startDateTd').text(calendarDateFrom + ' ' + calendarTimeFrom);
            $('#endDateTd').text(calendarDateTo + ' ' + calendarTimeTo);
            $('#commentTd').html($('#comment').val().replace(/\r?\n/g, '<br />'));
            $('#confirmModal').show();
        });

これは画面に用意した確認画面ボタンをクリックしたときの処理です。クリックすると非表示で用意していた確認画面用モーダルが表示され、フォームに入力された内容をモーダルにも表示しています。

どういうことかというと、

データを入力して確認画面を押します。そうすると

フォームに入力した内容がモーダルにも表示されると思います。

確認画面がないとユーザが間違えて入力していても気づかないので、用意した方が良いと思います。実際身の回りの申請フォームにも確認画面が用意されています。

Bulmaのモーダルを使うと簡単に実装できます。

        //確認モーダルのキャンセルボタンクリック時処理
        $('#confirmModal .cancel').on('click', function () {
            $('#confirmModal').hide();
        });

これは確認画面モーダルでキャンセルボタンか×アイコンをクリックしたときに、確認画面モーダルを非表示にしています。再入力ができるようになります。

        //確認モーダルの登録ボタンクリック時処理
        $('#registerButton').on('click', function () {
            $('.regist-loading-wrapper').show();
            const req = {};
            const params = {};
            $('#reserveForm').find('input').each(function (index, element) {
                const key = $(element).attr('name');
                const val = $(element).val();
                params[key] = val;
            });
            req.parameters = params;
            google.script.run.withSuccessHandler(doSubmitSuccess).doSubmitAjax(req);
        });
        const doSubmitSuccess = function (result) {
            //必要な場合、成功したときの画面処理を書く
        };

これは確認画面モーダルの登録ボタンをクリックしたときの処理です。

今回はフォームのサブミットで処理するのではなく、gasの非同期の機能を使ってgas(サーバサイド)の関数を実行しています。

実際gasの関数を呼び出している処理はこちらの部分です。

google.script.run.withSuccessHandler(doSubmitSuccess).doSubmitAjax(req);

これはgas上のdoSubmitAjaxを呼び出し、成功した場合にHTML上のdoSubmitSuccessが呼び出されるといった意味になります。

doSubmitAjaxにはパラメータを渡せるのでreqを渡しています。

ここからコード.gsに加えた以下の文につながる流れです。

function doSubmitAjax(req) {
    const params = req.parameters;
    const resObj = {};
    return resObj;
  }

GASでスプレッドシートに書き込む処理を作る

登録ボタンをクリックしたときにgasの関数を呼び出す処理が作れたので、doSubmitAjaxの関数に実際にスプレッドシートに書きこむ処理を加えていきます。

まずはコード.gsにスプレッドシートに書きこむ関数であるinsertRecordを追加してください。

  function insertRecord(param){
    let reservationTime = 0;
    const fromDate = new Date(param.calendar_date_from +' ' + param.calendar_time_from);
    const toDate = new Date(param.calendar_date_to + ' ' + param.calendar_time_to);
    //開始時間と終了時間までの時間を求める
    const diffDate = toDate.getTime() - fromDate.getTime();
    const diffMinute = Math.floor(diffDate / (60000));
    reservationTime = diffMinute/60;
    //この順番にスプレッドシートに格納される
    const data = [[
      param.user_id, 
      param.user_name, 
      param.calendar_date_from,
      param.calendar_time_from,
      param.calendar_date_to,
      param.calendar_time_to,
      param.comment,
      reservationTime,
      new Date()
    ]];
    //SPREAD_SHEET_IDは連携するスプレッドシートのID、SHEET_NAMEはシート名をそれぞれ置き換えてください。
    const app = SpreadsheetApp.openById('SPREAD_SHEET_ID');
    const sheet = app.getSheetByName('SHEET_NAME');
    const insertRow = sheet.getDataRange().getLastRow() + 1;  //挿入行
    const insertCol = 1;  //挿入列
    const insertRowNum = data.length;  //挿入行数
    const insertColNum = data[0].length;  //挿入列数(データ数)
    const insertRange = sheet.getRange(insertRow, insertCol,insertRowNum,insertColNum);
    //スプレッドシートに書きこむAPI
    insertRange.setValues(data);
  }

次にこの関数を呼び出すように先ほど作ったdoSubmitAjaxを修正します。

function doSubmitAjax(req) {
    const params = req.parameters;
    const resObj = {};
    //この1行を追加
    insertRecord(params);
    return resObj;
  }

これで完成です。

ではスプレッドシートに書きこむ処理を解説していきます。

    let reservationTime = 0;
    const fromDate = new Date(param.calendar_date_from +' ' + param.calendar_time_from);
    const toDate = new Date(param.calendar_date_to + ' ' + param.calendar_time_to);
    //開始時間と終了時間までの時間を求める
    const diffDate = toDate.getTime() - fromDate.getTime();
    const diffMinute = Math.floor(diffDate / (60000));
    reservationTime = diffMinute/60;

ここは時間を計算しているだけなので割愛します。

//この順番にスプレッドシートに格納される
    const data = [[
      param.user_id, 
      param.user_name, 
      param.calendar_date_from,
      param.calendar_time_from,
      param.calendar_date_to,
      param.calendar_time_to,
      param.comment,
      reservationTime,
      new Date()
    ]];

スプレッドシートに書きこむ変数を用意します。

スプレッドシートに用意したヘッダーに合わせて順番に配列に格納してください。

    //SPREAD_SHEET_IDは連携するスプレッドシートのID、SHEET_NAMEはシート名をそれぞれ置き換えてください。
    const app = SpreadsheetApp.openById('SPREAD_SHEET_ID');
    const sheet = app.getSheetByName('SHEET_NAME');
    const insertRow = sheet.getDataRange().getLastRow() + 1;  //挿入行
    const insertCol = 1;  //挿入列
    const insertRowNum = data.length;  //挿入行数
    const insertColNum = data[0].length;  //挿入列数(データ数)
    const insertRange = sheet.getRange(insertRow, insertCol,insertRowNum,insertColNum);

※SPREAD_SHEET_IDとSHEET_NAMEをそれぞれ置き換えてください。

まず連携するスプレッドシートのIDとシート名を取得します。

何行目の何列目から何行目の何列目までデータを入力するかを指定する必要があるのでそれぞれ変数に格納します。

変数を使いgetRangeメソッドで書き込む範囲を取得します。

    //スプレッドシートに書きこむAPI
    insertRange.setValues(data);

取得した範囲に対してsetValuesで書き込むことができます。

スプレッドシートを扱うAPIは同じ処理でもいくつか種類があるので実際にリファレンスを確認してもいいと思います。

GAS公式リファレンスはこちら

これで完成です!

完成ソース

コードが多くなってしまいわかりづらいと思うので、最後に完成したソースを乗っけておきます。

function doGet(e) {
  let page = 'index';
  const template = HtmlService.createTemplateFromFile(page);
  return template.evaluate();
}
function getAppUrl() {
  return ScriptApp.getService().getUrl();
}

function doSubmitAjax(req) {
    const params = req.parameters;
    const resObj = {};
    insertRecord(params);
    return resObj;
  }
  function insertRecord(param){
    let reservationTime = 0;
    const fromDate = new Date(param.calendar_date_from +' ' + param.calendar_time_from);
    const toDate = new Date(param.calendar_date_to + ' ' + param.calendar_time_to);
    const diffDate = toDate.getTime() - fromDate.getTime();
    const diffMinute = Math.floor(diffDate / (60000));
    reservationTime = diffMinute/60;
    //この順番にスプレッドシートに格納される
    const data = [[
      param.user_id, 
      param.user_name, 
      param.calendar_date_from,
      param.calendar_time_from,
      param.calendar_date_to,
      param.calendar_time_to,
      param.comment,
      reservationTime,
      new Date()
    ]];
    //SPREAD_SHEET_IDは連携するスプレッドシートのID、SHEET_NAMEはシート名をそれぞれ置き換えてください。
    const app = SpreadsheetApp.openById('SPREAD_SHEET_ID');
    const sheet = app.getSheetByName('SHEET_NAME');
    const insertRow = sheet.getDataRange().getLastRow() + 1;  //挿入行
    const insertCol = 1;  //挿入列
    const insertRowNum = data.length;  //挿入行数
    const insertColNum = data[0].length;  //挿入列数(データ数)
    const insertRange = sheet.getRange(insertRow, insertCol,insertRowNum,insertColNum);
    insertRange.setValues(data);
  }

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>シェアキッチン予約フォーム</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://use.fontawesome.com/releases/v5.3.1/js/all.js" defer></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.min.css" />
    <link rel="stylesheet"
        href="https://cdnjs.cloudflare.com/ajax/libs/jquery-timepicker/1.13.16/jquery.timepicker.min.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-timepicker/1.13.16/jquery.timepicker.min.js"></script>
</head>

<body class="is-flex is-justify-content-center has-background-light">
    <div class="main has-background-white is-flex-direction-column">
        <div class="conteiner form-title">
            <section class="hero is-info">
                <div class="hero-body">
                    <div class="container">
                        <h1 class="title">
                            シェアキッチン予約フォーム(β版)
                        </h1>
                    </div>
                </div>
            </section>
        </div>
        <div id="registerContent" class="conteiner form-body">
            <form id="reserveForm" action="<?= getAppUrl() ?>" method="post">
                <div class="field">
                    <label class="label" for="userId">ユーザID(登録メールアドレス)<span class="tag is-danger">必須</span></label>
                    <div class="control has-icons-left">
                        <input class="input" type="email" id="userId" name="user_id" placeholder="Email" />
                        <span class="icon is-small is-left">
                            <i class="fas fa-envelope"></i>
                        </span>
                    </div>
                </div>
                <div class="field">
                    <label class="label" for="userName">氏名<span class="tag is-danger">必須</span></label>
                    <div class="control has-icons-left">
                        <input class="input" type="text" id="userName" name="user_name" placeholder="Name" />
                        <span class="icon is-small is-left">
                            <i class="fas fa-user"></i>
                        </span>
                    </div>
                </div>
                <div class="field calendar-field">
                    <label class="label" for="">開始日時<span class="tag is-danger">必須</span></label>
                    <div class="control calendar-control is-flex is-flex-wrap-nowrap">
                        <div><input class="input" type="date" id="calendarDateFrom" name="calendar_date_from" />
                            <input class="timepicker input" type="text" name="calendar_time_from" id="calendarTimeFrom"
                                data-time-format="H:i">
                        </div>
                        <label class="label" for="">終了日時<span class="tag is-danger">必須</span></label>
                        <div>
                            <input class="input" type="date" id="calendarDateTo" name="calendar_date_to" />
                            <input class="timepicker input" type="text" name="calendar_time_to" id="calendarTimeTo"
                                data-time-format="H:i">
                        </div>
                    </div>
                </div>
                <div class="field">
                    <label class="label" for="comment">備考</label>
                    <div class="control">
                        <textarea class="textarea " id="comment" name="comment" placeholder="Comment"></textarea>
                    </div>
                </div>
                <div class="field">
                    <div class="control">
                        <button type="button" id="submitButton" class="submit-button button is-info">確認画面</button>
                    </div>
                    <div class="modal" id="confirmModal">
                        <div class="modal-background"></div>
                        <div class="modal-card">
                            <header class="modal-card-head">
                                <p class="modal-card-title">確認</p>
                                <button type="button" class="delete cancel" aria-label="close"></button>
                            </header>
                            <section class="modal-card-body">
                                <table class="table is-striped is-fullwidth">
                                    <tbody>
                                        <tr>
                                            <th>ユーザID(登録メールアドレス)</th>
                                            <td id="userIdTd"></td>
                                        </tr>
                                        <tr>
                                            <th>氏名</th>
                                            <td id="userNameTd"></td>
                                        </tr>
                                        <tr>
                                            <th>開始日時</th>
                                            <td id="startDateTd"></td>
                                        </tr>
                                        <tr>
                                            <th>終了日時</th>
                                            <td id="endDateTd"></td>
                                        </tr>
                                        <tr>
                                            <th>備考</th>
                                            <td id="commentTd"></td>
                                        </tr>
                                    </tbody>
                                </table>
                            </section>
                            <footer class="modal-card-foot">
                                <button type="button" id="registerButton" class="button is-success">登録</button>
                                <button type="button" class="button cancel">キャンセル</button>
                            </footer>
                        </div>
                    </div>
                </div>
            </form>
        </div>
    </div>
    <script>
    $(function () {
        $('.timepicker').timepicker();
        //確認画面ボタンクリック時処理
        $('#submitButton').on('click', function () {
            let isError = false;
            let errorMessage = [];
            const userId = $('#userId').val();
            const userName = $('#userName').val();
            const calendarDateFrom = $('#calendarDateFrom').val();
            const calendarTimeFrom = $('#calendarTimeFrom').val();
            const calendarDateTo = $('#calendarDateTo').val();
            const calendarTimeTo = $('#calendarTimeTo').val();
            const now = new Date();
            const fullStartDate =  new Date(calendarDateFrom + ' '+ calendarTimeFrom+ ':00');
            const fullEndDate =  new Date(calendarDateTo + ' '+ calendarTimeTo+ ':00');
            $('#userIdTd').text(userId);
            $('#userNameTd').text(userName);
            $('#startDateTd').text(calendarDateFrom + ' ' + calendarTimeFrom);
            $('#endDateTd').text(calendarDateTo + ' ' + calendarTimeTo);
            $('#commentTd').html($('#comment').val().replace(/\r?\n/g, '<br />'));
            $('#confirmModal').show();
        });
        //確認モーダルのキャンセルボタンクリック時処理
        $('#confirmModal .cancel').on('click', function () {
            $('#confirmModal').hide();
        });
        //確認モーダルの登録ボタンクリック時処理
        $('#registerButton').on('click', function () {
            $('.regist-loading-wrapper').show();
            const req = {};
            const params = {};
            $('#reserveForm').find('input').each(function (index, element) {
                const key = $(element).attr('name');
                const val = $(element).val();
                params[key] = val;
            });
            req.parameters = params;
            google.script.run.withSuccessHandler(doSubmitSuccess).doSubmitAjax(req);
        });
    });
    const doSubmitSuccess = function (result) {
        //必要な場合、成功したときの画面処理を書く
    };

</script>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        body {
            width: 100vw;
            min-height: 100vh;
        }

        .main {
            margin: 20px;
            width: 60%;
            box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
            border-radius: 6px;
        }

        .form-body {
            padding: 20px;
        }

        #reserveForm {
            width: 100%;

        }

        .is-justify-content-center {
            justify-content: center;
        }

        .is-flex-direction-column {
            flex-direction: column;
        }

        .calendar-field input[type=date] {
            width: 160px;
            margin: 10px auto;
        }

        .calendar-field .timepicker {
            width: 100px;
            margin: 10px 10px 10px 0;
        }

        .form-title .hero-body {
            padding: 1.4rem 0.8rem;
        }

        .form-title .title {
            font-size: 1.2rem;
        }

        .form-body .field:not(:last-child) {
            margin-bottom: 1.5rem;
        }


        .is-flex-wrap-nowrap {
            flex-wrap: wrap;
        }

        .form-body #userId {
            width: 60%;
        }

        .form-body .submit-button {
            padding-left: 3em;
            padding-right: 3em;
        }

        .calendar-control>div {
            width: 100%;
        }


        /*レスポンシブ(スマホ)*/
        @media screen and (max-width: 480px) {
            body {
                min-height: 100%;
            }

            .main {
                width: 100%;
            }

            .form-body .form-text {
                display: none;
            }
        }
    </style>

</body>

</html>

最後に

今回はフォームを作成してgasでスプレッドシートに書きこむ処理を作成しました。

実際運用する場合はここにバリデーションをつけたり、登録に画面を切り替えたり、非同期通信が失敗したときの処理を書いたりともう少し書かないといけないですが、スプレッドシートをデータベース代わりに申請フォームを作成できるのは使い方が広がると思います。

友人に頼まれて実際作ったものはさらに登録したらユーザにメールを送信してGoogleカレンダーに登録する処理まで作りました。この辺は今度どこかで記事を書こうと思います。

長くなってしまいましたが、読んでいただきありがとうございました!

スポンサーリンク
GoogleAppsScript プログラミング
スポンサーリンク
この記事を書いた人
まさき

25歳の社会人3年目のシステムエンジニアです。
未経験からIT企業に入社して業務や、業務外でも学習することでプログラミングが書けるようになりました。
得意なのはHTML,CSS,JavaScript。最近はVue.jsとGASを勉強中です。
本を読むことが好きなのでIT技術書以外にもたくさん読みます。
好奇心旺盛でとりあえずやってみる精神です。
楽しいことが生きがいで、仕事も私生活も楽しくなるように日々奮闘中。

まさきをフォローする
シェアする
まさきのエンジニア図書館
タイトルとURLをコピーしました