GASのHtmlServiceで
入力フォーム画面を作る方法

GASだけで自社専用の入力画面を作れます。HtmlService(GASからHTML画面を返す仕組み)とgoogle.script.runを使い、画面からスプレッドシートへ保存するまでを動くコードで解説します。

|対象: GAS / HtmlService / Webアプリ

Table of Contents

HtmlServiceでできること・向いている場面

HtmlServiceは、GASからHTMLの画面を返すための仕組みです。サーバーを借りなくても、Googleアカウントだけで自社専用のWeb画面を公開できます。 「Googleフォームでは足りないが、システムを作るほどではない」場面にちょうど当てはまります。

入力途中で計算したい

単価×数量の自動計算や、在庫数のリアルタイム表示など、フォームでは実現できない挙動を作れる

既存データを表示したい

スプレッドシートの現在の値を画面に出し、その場で確認・修正してもらえる

見た目を業務に合わせたい

自社のロゴや配色、項目の並びを自由に設計でき、現場の入力ミスを減らせる

スマホから使わせたい

viewportを設定すればスマホでも崩れず、現場からの報告入力画面として使える

doGetでHTML画面を返す最小構成

Webアプリとして公開したURLにブラウザでアクセスすると、GASのdoGet関数が呼ばれます。ここでHTMLを返せば画面が表示されます。 まずはスクリプトエディタで「ファイル」→「HTML」からindex.htmlを作り、次のコードを書きます。

// コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index")
    .setTitle("備品申請フォーム")
    .addMetaTag("viewport", "width=device-width, initial-scale=1");
}

createHtmlOutputFromFileは指定したHTMLファイルをそのまま返します。addMetaTag("viewport", ...)はスマホ表示のために必須です。HTMLファイル内に直接書いたmetaタグは無視されるため、この書き方で指定します。

画面(HTML)側を作る

画面は普通のHTMLで書けます。ここでは備品申請フォームを例にします。 各入力欄のname属性は、後でサーバー側が受け取るキーになるため必ず付けます。

<!-- index.html -->
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <style>
      body { font-family: sans-serif; max-width: 480px; margin: 40px auto; }
      label { display: block; margin-top: 16px; font-size: 14px; }
      input, select { width: 100%; padding: 8px; box-sizing: border-box; }
      button { margin-top: 24px; padding: 10px 20px; }
    </style>
  </head>
  <body>
    <h1>備品申請</h1>
    <form id="requestForm">
      <label>申請者名
        <input type="text" name="applicant" required>
      </label>
      <label>品名
        <input type="text" name="item" required>
      </label>
      <label>数量
        <input type="number" name="quantity" value="1" min="1" required>
      </label>
      <button type="submit">送信する</button>
    </form>
    <p id="message"></p>
  </body>
</html>

<base target="_top">は忘れずに入れてください。GASの画面はiframe(ページ内に埋め込まれた別ページ)の中で動くため、これがないとリンクが小さな枠の中で開いてしまいます。

google.script.runでサーバー関数を呼ぶ

画面側からGASの関数を呼ぶにはgoogle.script.runを使います。非同期で動くため戻り値はwithSuccessHandlerのコールバックで受け取り、失敗はwithFailureHandlerで拾います。

<!-- index.html の </body> の直前に追加 -->
<script>
  document.getElementById("requestForm").addEventListener("submit", function (e) {
    e.preventDefault();

    var button = e.target.querySelector("button");
    button.disabled = true;
    setMessage("送信中...");

    google.script.run
      .withSuccessHandler(function (result) {
        setMessage(result.message);
        e.target.reset();
        button.disabled = false;
      })
      .withFailureHandler(function (error) {
        setMessage("エラー: " + error.message);
        button.disabled = false;
      })
      .saveRequest(e.target); // form要素をそのまま渡せる
  });

  function setMessage(text) {
    document.getElementById("message").textContent = text;
  }
</script>

ポイントは、form要素をそのまま引数に渡している点です。GASが各inputのnameをキーにしたオブジェクトへ自動変換してくれるため、項目が増えてもコードを直す必要がありません。 送信中はボタンをdisabledにして、二重送信を防いでいます。

受け取った入力値をスプレッドシートに保存する

サーバー側では、受け取ったオブジェクトを検証してからappendRowで1行追記します。HTMLのrequired属性はブラウザ側の簡易チェックにすぎないため、サーバー側でも必ず検証します。

// コード.gs
function saveRequest(formObject) {
  const sheet = SpreadsheetApp.getActive().getSheetByName("申請一覧");

  // 画面から来た値は必ずサーバー側でも検証する
  const applicant = String(formObject.applicant || "").trim();
  const item = String(formObject.item || "").trim();
  const quantity = Number(formObject.quantity);

  if (!applicant || !item) {
    throw new Error("申請者名と品名は必須です");
  }
  if (!(quantity > 0)) {
    throw new Error("数量は1以上の数値で入力してください");
  }

  sheet.appendRow([
    new Date(),
    applicant,
    item,
    quantity,
    Session.getActiveUser().getEmail(),
    "未処理",
  ]);

  return { message: applicant + " さんの申請を受け付けました。" };
}

サーバー側でthrow new Error(...)すると、画面側のwithFailureHandlerが呼ばれ、そのメッセージがerror.messageに入ります。入力エラーの伝え方としてそのまま使えます。

テンプレートで既存データを画面に埋め込む

プルダウンの選択肢をスプレッドシートで管理したい場合は、createTemplateFromFileを使います。HTMLを返す前にサーバー側の値を差し込めるため、画面表示後に読み込む方式より表示が速くなります。

// コード.gs
function doGet() {
  const template = HtmlService.createTemplateFromFile("index");
  template.categories = getCategories(); // テンプレートに値を渡す
  return template.evaluate().setTitle("備品申請フォーム");
}

function getCategories() {
  return SpreadsheetApp.getActive()
    .getSheetByName("カテゴリ")
    .getRange("A2:A")
    .getValues()
    .flat()
    .filter(String);
}

HTML側ではスクリプトレット(テンプレート内でGASのコードを書く記法)で値を展開します。<? ?>は処理だけ、<?= ?>は値をエスケープして出力します。

<!-- index.html -->
<label>カテゴリ
  <select name="category">
    <? categories.forEach(function (name) { ?>
      <option value="<?= name ?>"><?= name ?></option>
    <? }); ?>
  </select>
</label>

CSS・JSを別ファイルに分けるincludeパターン

GASのHTMLファイルは.css.jsを直接置けません。そこでstyle.htmlscript.htmlとしてHTMLファイルを分け、次のinclude関数で読み込むのが定番です。1ファイルが長くなりすぎるのを防げます。

// コード.gs
function include(filename) {
  return HtmlService.createHtmlOutputFromFile(filename).getContent();
}

呼び出し側は<?!= ?>(エスケープせずにそのまま出力する記法)を使います。この方法はcreateTemplateFromFileで読み込んだときだけ動く点に注意してください。

<!-- index.html -->
<head>
  <base target="_top">
  <?!= include("style"); ?>
</head>
<body>
  <!-- 画面の中身 -->
  <?!= include("script"); ?>
</body>

公開設定と実務での注意点

1. 「実行するユーザー」の選び方で権限が変わる

デプロイ時に「自分」を選ぶと、アクセスした人にシートの閲覧権限がなくても書き込めます。 社内の誰でも申請できる画面にするならこちらです。一方「アクセスしているユーザー」を選ぶと、その人自身の権限で動くため、シートの共有設定が必要になります。

2. コードを直したらデプロイを更新する

公開URLは保存した時点のバージョンを表示します。修正が反映されないときは「デプロイを管理」から新しいバージョンに更新してください。 動作確認だけならエディタの「テストデプロイ」を使うと、公開URLに影響を与えずに試せます。

3. 同時アクセスの書き込み競合に備える

複数人が同時に送信すると、行の取り合いでデータが壊れることがあります。LockServiceで排他制御をかけるか、追記だけで済む設計にしておくと安全です。

4. iframeの制約を先に確認する

GASの画面はiframe内で動くため、カメラ起動など一部のブラウザ機能が制限されます。 実装したい機能がある場合は、作り込む前に小さな検証コードで動くかどうかを確かめておくと手戻りを防げます。

まとめ

HtmlServiceを使えば、doGetでHTMLを返し、google.script.runでサーバー関数を呼ぶだけで、自社専用の入力画面が作れます。 テンプレートで既存データを埋め込み、includeでファイルを分割すれば、項目が増えても管理しやすい構成を保てます。 サーバー側の入力検証・デプロイ更新・同時アクセス対策の3点を押さえておけば、日々の業務で使える申請・報告画面として十分に運用できます。

よくある質問

アンケートのように「回答を集めるだけ」ならGoogleフォームで十分です。既存データを画面に表示したい、入力途中で在庫や単価を計算したい、ログインユーザーによって表示を変えたい、といった要件が出てきたらHtmlServiceの出番です。HtmlServiceは自分でHTMLとJavaScriptを書ける分、画面の自由度が高くなります。

google.script.run(画面側からサーバー側のGAS関数を呼ぶ仕組み)は非同期で動くため、return では値を受け取れません。withSuccessHandler(コールバック関数) を先に指定してから関数を呼び、コールバックの引数として結果を受け取ります。エラー時は withFailureHandler が呼ばれます。

あります。渡せるのは文字列・数値・真偽値・日付・配列・単純なオブジェクトまでです。DOM要素や関数はそのまま渡せません。フォーム全体を渡したい場合は、form要素をそのまま引数にすると各inputのname属性をキーにしたオブジェクトへ自動変換されます。

エディタの「デプロイ」→「新しいデプロイ」で種類に「ウェブアプリ」を選び、実行ユーザー(自分/アクセスしているユーザー)とアクセスできるユーザーを設定するとURLが発行されます。コードを直した後は「デプロイを管理」からバージョンを更新しないと、公開URLの内容は古いままになります。

読み込めます。HTML内のlinkタグやscriptタグでCDNのURLを指定すれば利用できます。ただしGASのWebアプリはiframe(ページ内に埋め込まれた別ページ)の中で動くため、カメラやクリップボードなど一部のブラウザAPIは制限されます。動作させたい機能がiframe内で使えるか、事前に確認しておくと安全です。

GASで自社専用の
業務画面を作る。

申請フォーム・在庫管理・日報入力など、サーバーを持たずに運用できる業務Web画面の開発をご相談いただけます。

GAS Webシステム開発を見る