google.script.runでHTMLフォームからGASを呼び出す方法

公開日: 

google.script.runでHTMLフォームからGASを呼び出す方法。フォームとGASの通信および登録結果を示すアイキャッチ

第4回で作成した入力フォームから google.script.run でGAS側の saveRequest(data) を呼び出し、作業依頼シートへ登録します。

今回は、登録中の表示、ボタンの無効化、登録後のフォーム初期化に加え、成功・失敗ハンドラーを実装します。

シリーズ全体の構成と完成画面は第0回で紹介しています。

1. 今回作るもの

ファイル用途
Code.gssaveRequest(data) で登録データを処理する
index.html入力フォーム。script.html を読み込む
style.html登録中、成功、エラー表示の見た目を追加
script.htmlgoogle.script.run でGASを呼び出す

2. 完成イメージ

  • フォームの入力値をJavaScriptオブジェクトにできる
  • 入力不足を画面側で確認できる
  • 登録中にボタンを無効化できる
  • google.script.run で saveRequest(data) を呼び出せる
  • 成功時にメッセージを表示し、フォームを初期化できる
  • 失敗時にエラーメッセージを表示できる
作業依頼受付フォームに顧客、件名、依頼内容、担当者を入力した状態

3. 前回までの状態

第4回では、HTML Serviceを使って作業依頼受付フォームを表示できるようにしました。

第5回の starter.zip には、第4回完了時点の Code.gs、index.html、style.html を入れています。

4. 変更するファイル

第4回から更新するのは index.html と style.html です。新しく script.html を作成します。Code.gs は第4回から変更せず、既存の saveRequest(data) を使います。

script.html には、入力値の取得、入力チェック、GAS関数の呼び出し、結果の表示をまとめます。

5. データの流れ

入力フォーム
  ↓
script.htmlで入力値を集める
  ↓
google.script.runでsaveRequest(data)を呼び出す
  ↓
Code.gsがappendRow()で作業依頼シートに1行追加する
  ↓
Code.gsが結果オブジェクトを返す
  ↓
script.htmlが結果に応じたメッセージを表示する

google.script.run は、HTML Serviceの画面からGAS側の関数を呼び出す仕組みです。GAS側の処理を待つ間も画面を操作でき、結果は後からハンドラーで受け取ります。詳しくはGoogle公式の通信ガイドを参照してください。

6. 貼り付けるコード

index.html

index.html は、第4回で作成したファイルを次のように更新します。フォームに id を付け、script.html を読み込む形です。

第4回からの変更点は次のとおりです。

  • <form> に id="requestForm" を追加
  • 顧客、件名、依頼内容に required を追加
  • 顧客の <option> に data-name を追加
  • 登録ボタンを type="submit" に変更し、id="submitButton" を追加
  • メッセージ欄に id="formMessage" を追加
  • <?!= include('script'); ?> を追加

data-name により、JavaScript側で「顧客コード」と「顧客名」の両方を取得できます。

<!doctype html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <base target="_top">
  <?!= include('style'); ?>
</head>
<body>
  <main class="page">
    <section class="form-panel" aria-labelledby="page-title">
      <p class="eyebrow">作業依頼・受付管理Webアプリ</p>
      <h1 id="page-title">作業依頼受付フォーム</h1>
      <p class="lead">必要事項を入力して、作業依頼を登録します。</p>

      <form id="requestForm">
        <div class="field">
          <label for="customer">顧客</label>
          <select id="customer" name="customer" required>
            <option value="">選択してください</option>
            <option value="C001" data-name="サンプル商事">サンプル商事</option>
            <option value="C002" data-name="テスト工業">テスト工業</option>
            <option value="C003" data-name="デモサービス">デモサービス</option>
          </select>
        </div>

        <div class="field">
          <label for="title">件名</label>
          <input id="title" name="title" type="text" placeholder="例:資料作成の依頼" required>
        </div>

        <div class="field">
          <label for="description">依頼内容</label>
          <textarea id="description" name="description" rows="6" placeholder="依頼したい作業内容を入力してください" required></textarea>
        </div>

        <div class="field">
          <label for="assignee">担当者</label>
          <input id="assignee" name="assignee" type="text" placeholder="例:山田">
        </div>

        <button id="submitButton" type="submit">登録する</button>
        <p id="formMessage" class="message">入力後に登録ボタンを押してください。</p>
      </form>
    </section>
  </main>
  <?!= include('script'); ?>
</body>
</html>

script.html

<script>
  const form = document.getElementById('requestForm');
  const submitButton = document.getElementById('submitButton');
  const message = document.getElementById('formMessage');

  form.addEventListener('submit', (event) => {
    event.preventDefault();

    const requestData = buildRequestData();
    const validationMessage = validateRequestData(requestData);

    if (validationMessage) {
      showMessage(validationMessage, 'error');
      return;
    }

    setSubmitting(true);
    showMessage('登録しています。しばらくお待ちください。', 'info');

    google.script.run
      .withSuccessHandler(handleSaveSuccess)
      .withFailureHandler(handleSaveFailure)
      .saveRequest(requestData);
  });

  function buildRequestData() {
    const customerSelect = document.getElementById('customer');
    const selectedOption = customerSelect.options[customerSelect.selectedIndex];

    return {
      customerCode: customerSelect.value,
      customerName: selectedOption ? selectedOption.dataset.name || '' : '',
      title: document.getElementById('title').value.trim(),
      description: document.getElementById('description').value.trim(),
      assignee: document.getElementById('assignee').value.trim(),
    };
  }

  function validateRequestData(data) {
    if (!data.customerCode) {
      return '顧客を選択してください。';
    }
    if (!data.title) {
      return '件名を入力してください。';
    }
    if (!data.description) {
      return '依頼内容を入力してください。';
    }
    return '';
  }

  function handleSaveSuccess(result) {
    setSubmitting(false);

    if (!result || !result.success) {
      showMessage(result && result.message ? result.message : '登録に失敗しました。', 'error');
      return;
    }

    form.reset();
    showMessage('登録しました。', 'success');
  }

  function handleSaveFailure(error) {
    setSubmitting(false);
    showMessage(error && error.message ? error.message : '登録中にエラーが発生しました。', 'error');
  }

  function setSubmitting(isSubmitting) {
    submitButton.disabled = isSubmitting;
    submitButton.textContent = isSubmitting ? '登録中...' : '登録する';
  }

  function showMessage(text, type) {
    message.textContent = text;
    message.className = 'message';

    if (type === 'success') {
      message.classList.add('is-success');
    }
    if (type === 'error') {
      message.classList.add('is-error');
    }
  }
</script>

style.html

style.html には、登録中のボタン表示と、成功・失敗メッセージの色を追加します。第4回で作成した style.html に、次のCSSを追加してください。

button:disabled {
  background: #94a3b8;
  cursor: not-allowed;
}

.message.is-success {
  background: #ecfdf5;
  color: #065f46;
}

.message.is-error {
  background: #fef2f2;
  color: #991b1b;
}

ここまでの内容をまとめた完成版は complete.zip に入れています。

7. コードの解説

送信時にブラウザの画面遷移を止める

form.addEventListener('submit', ...) は、フォームが送信されたときに実行する処理を登録します。登録ボタンは type="submit" なので、クリックすると submit イベントが発生します。キーボードでフォームを送信した場合も同じイベントが発生します。

顧客、件名、依頼内容にはHTMLの required 属性を付けています。空欄があると、ブラウザが送信を止め、submit イベントの処理は実行されません。空白だけの入力は required では空欄と判定されないため、JavaScript側で trim() した値も確認しています。

event.preventDefault() は、フォーム送信後にブラウザがページを移動・再読み込みする動作を止めます。代わりにJavaScriptからGASを呼び出すため、入力画面をそのまま表示して結果を知らせられます。入力が不足している場合はメッセージを出して return し、GASを呼び出しません。

入力値をオブジェクトにまとめる

buildRequestData() は、各入力欄の値を saveRequest(data) に渡すオブジェクトへまとめます。

オブジェクトのキーフォームからの取得元送信する値
customerCode顧客の <select>選択肢の value(顧客コード)
customerName選択中の <option>data-name 属性の顧客名
title件名の <input>前後の空白を除いた文字列
description依頼内容の <textarea>前後の空白を除いた文字列
assignee担当者の <input>前後の空白を除いた文字列。未入力でもよい

顧客の <option> は、画面に表示する顧客名をテキストに、顧客コードを value に持ちます。data-name から顧客名も読むため、GAS側へコードと名前の両方を渡せます。data-* はHTML要素に追加できるカスタムデータ属性で、JavaScriptでは dataset.name として読み取ります。

google.script.runは非同期で動く

google.script.run は、GAS側の saveRequest(requestData) を呼び出します。呼び出しを書いた直後に結果が返るわけではありません。結果が届くまでに時間がかかるため、呼び出しの前に setSubmitting(true) でボタンを無効にし、「登録しています」と表示します。

withSuccessHandler(handleSaveSuccess) は、GAS側の関数呼び出しが正常に完了したときに実行され、戻り値を result として受け取ります。登録できたかどうかは result.success を見て判定します。withFailureHandler(handleSaveFailure) は、GAS側で例外が発生するなど、呼び出し自体が失敗した場合の処理を登録します。google.script.run は非同期なので、戻り値をその場で変数に代入せず、ハンドラーで処理します。

GAS側で入力を確認して行を追加する

サーバー側の saveRequest(data) は、第3回で作成した処理を使います。GAS側でも顧客コード、顧客名、件名、依頼内容を確認してから 作業依頼 シートを取得します。担当者は必須項目ではありません。

シートが見つからない場合や必須項目が不足している場合、saveRequest(data) は { success: false, message: '...' } を返します。登録できる場合は Utilities.getUuid() で内部IDを作り、登録日時と更新日時に同じ Date を設定します。その後、appendRow() で作業依頼シートに1行追加します。ステータスは 未対応、受付番号は第7回で採番するため空欄です。

登録できた場合は { success: true, id: ..., message: '登録しました。' } を返します。saveRequest() は発生したエラーを catch で捕捉し、{ success: false, message: '...' } として返します。関数は結果オブジェクトを返して正常に終了するため、クライアント側では withSuccessHandler() が実行され、そこで result.success を判定します。

戻り値に応じて画面を更新する

handleSaveSuccess(result) は、まず setSubmitting(false) でボタンを再び押せる状態にします。result.success が true なら form.reset() で入力欄を初期状態に戻し、「登録しました。」と表示します。success が false の場合は、サーバーが返した result.message をエラー表示に使い、入力欄は残します。

handleSaveFailure(error) は、GAS側の関数が例外を投げた場合や、呼び出し自体に失敗した場合に実行されます。エラーの内容を表示し、ボタンを再び押せる状態に戻します。saveRequest() が返す success: false は通常の戻り値なので、こちらではなく handleSaveSuccess() 内で処理します。

showMessage() は、メッセージ本文を textContent に設定し、種類に応じて is-success または is-error クラスを追加します。style.html はこのクラスを使って成功・失敗メッセージの色を変えます。登録中のメッセージは基本の .message の見た目です。

登録中は送信ボタンを無効にするため、その画面で処理中に続けてクリックする操作を抑えられます。ただし、これはサーバー側の重複登録判定ではありません。別タブからの送信や、結果が分からないときに再送した場合の重複までは防ぎません。

8. 導入手順

  1. Apps Scriptエディタを開く
  2. index.html と style.html を第5回の内容に更新する(Code.gs は第4回から変更ありません)
  3. HTMLファイルとして script.html を作成する
  4. 保存する
  5. まだ setupAppSheets() を実行していない場合は実行する
  6. テストデプロイで画面を確認する
  7. 既存のWebアプリURLを使う場合は、デプロイを編集して新しいバージョンを選び、再デプロイする
  8. 顧客、件名、依頼内容、担当者を入力して登録する

最新のコードをテストデプロイで確認する

保存した最新コードを確認するには、Apps Scriptエディタの「デプロイ」からテストデプロイを開き、種類に「ウェブアプリ」を選びます。テスト用URLの末尾は /dev で、スクリプトを編集できるユーザーがアクセスできます。詳しくはGoogle公式のウェブアプリガイドを参照してください。

既存のWebアプリURLへ変更を反映する

第4回で作成した通常のWebアプリURLを使い続ける場合、コードを保存しただけでは変更後の版に切り替わりません。「デプロイの管理」で対象のデプロイを編集し、新しいバージョンを選んで再デプロイします。既存のデプロイを更新すれば、URLを変えずに新しいコードを反映できます。詳しい手順はGoogle公式のデプロイ管理ガイドを参照してください。

9. 動作確認

登録すると、ボタンが 登録中... に変わり、処理中は押せない状態になります。

登録中の入力フォーム。登録ボタンが「登録中...」になり、処理中メッセージが表示されている

登録後、画面に 登録しました。 と表示されれば成功です。入力欄は初期化され、続けて次の依頼を入力できます。

登録成功後の入力フォーム。「登録しました。」と表示され、入力欄が初期化されている

次に、作業依頼 シートを確認します。新しい行が追加され、内部ID、登録日時、更新日時、顧客コード、顧客名、件名、依頼内容、ステータス、担当者が入っていれば登録できています。

作業依頼シートに登録した行が追加された状態。内部ID、登録日時、顧客コード、顧客名、件名、依頼内容、ステータス、担当者が入っている

受付番号は第7回で追加するため、今回も空欄で問題ありません。

10. よくあるエラー

登録ボタンを押しても反応しない

index.html に <?!= include('script'); ?> が入っているか確認してください。

google is not defined と表示される

ローカルHTMLとして直接開いている可能性があります。GASのHTML Service上で表示してください。

作業依頼シートが見つからない

先に setupAppSheets() を実行してください。

「顧客名を入力してください」と表示される

<option> に data-name 属性が付いていない可能性があります。

<option value="C001" data-name="サンプル商事">サンプル商事</option>

のように、value とは別に data-name 属性で顧客名を設定してください。

同じデータが複数登録される

登録中にボタンが無効になるのは、同じ画面からの連続クリックを抑えるためです。別タブからの送信や、結果が分からないときの再送による重複登録までは防げません。ボタンが「登録中…」に変わるかを確認し、別の画面や再送を含む重複を防ぐ処理は別途検討してください。

11. starter.zipの案内

starter.zip には、第4回完了時点の Code.gs、index.html、style.html、README.txt を入れています。

starter.zipをダウンロード

12. complete.zipの案内

complete.zip には、Code.gs、index.html、style.html、script.html、README.txt を入れています。

complete.zipをダウンロード

13. 次回追加する機能

次回は、顧客マスタシートから有効な顧客だけを取得し、プルダウンへ表示できるようにします。




Your Message

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください。

スポンサードリンク

記事が気に入ったらシェアお願いします

PAGE TOP ↑