今までの記事一覧
Googleフォーム×GASで実現する業務自動化シリーズ全記事まとめ目次
前提
この記事は、フォーム回答を保存しているスプレッドシート側のGASを前提にしています。
トリガーは以下を設定してください。
- 実行する関数:
onFormSubmit - イベントのソース:スプレッドシートから
- イベントの種類:フォーム送信時
スプレッドシート構成
スプレッドシート「集計シート」をこの図のように設定しています。
- B1セルに残数が入る
- 4行目以下にフォームから送信された注文内容が記録される
おさらい
前回は、フォームの「回答の検証」機能をGASで「1以上残数以下」に更新し、残数を超える送信をブロックする機能を作りました。
今回は、自由入力(記述式)をやめて、プルダウンやラジオボタンを動的に変更する方法を考えてみましょう。
やりたいこと
前回までのフォームで、ラジオボタン/プルダウンになっている「注文数を入力してください」の質問部分の選択肢を、残数に応じて「1、2、・・・残数」と動的に変更する。
フォームはあらかじめラジオボタン/プルダウンに直しておきましょう。
質問名も「注文数を入力してください」から「注文数を選択してください」にしたほうがいいですね。
今回使うメソッドはコレ!
ラジオボタン方式
-
asMultipleChoiceItem()
質問をラジオボタン型として取得します。 -
setChoiceValues()
選択肢をセットします。
ここを変える
「注文数を選択してください」の質問をラジオボタンとしてmcItemに格納
(前回textItemとしていた変数名を、ラジオボタンに合わせてmcItemにしています)
const mcItem = item.asMultipleChoiceItem();
選択肢は配列化します。数値のままでは選択肢に入れることができないので、String を使って文字列にしてchoicesに格納します。
const choices = [];
for (let i = 1; i <= newRemaining; i++) {
choices.push(String(i)); // "1", "2", "3" ...と文字列で入れる
}
フォーム上のラジオボタンを書き換えます。
mcItem.setChoiceValues(choices);
前回のelseの中をこのように書き換えます。
} else { //残数がまだある場合
//フォーム全体の「説明欄」を書き換える場合
form.setDescription("【現在の残り在庫: " + newRemaining + " 個】");
//特定の質問(注文数)の「ラジオボタン選択肢」を書き換える
const items = form.getItems();
for (const item of items) {
if (item.getTitle() === "注文数を選択してください") {
// ラジオボタン(asMultipleChoiceItem)に型変換
const mcItem = item.asMultipleChoiceItem();
// 1 から newRemaining までの「文字列の配列」を作る
const choices = [];
for (let i = 1; i <= newRemaining; i++) {
choices.push(String(i)); // "1", "2", "3" ...と文字列で入れる
}
// ラジオボタンの選択肢を最新在庫に合わせて書き換え
mcItem.setChoiceValues(choices);
// ヘルプテキスト(説明文)を更新
mcItem.setHelpText("※現在の残り在庫は「あと " + newRemaining + " 個」です。");
break;
}
}
}
プルダウン方式
-
asListItem()
質問をプルダウン型として取得します。 -
setChoiceValues()
選択肢をセットします。
ここを変える
「注文数を選択してください」の質問をプルダウンとしてlistItemに格納
(前回textItemとしていた変数名を、プルダウンに合わせてlistItemにしています)
const listItem = item.asListItem();
選択肢は配列化します。数値のままでは選択肢に入れることができないので、String を使って文字列にしてchoicesに格納します。これはラジオボタンと同じです。
const choices = [];
for (let i = 1; i <= newRemaining; i++) {
choices.push(String(i)); // "1", "2", "3"... と文字列で入れていく
}
フォーム上のプルダウンリストを書き換えます。
listItem.setChoiceValues(choices);
前回のelseの中をこのように書き換えます。
} else { //残数がまだある場合
//フォーム全体の「説明欄」を書き換える場合
form.setDescription("【現在の残り在庫: " + newRemaining + " 個】");
//特定の質問(注文数)の「プルダウン選択肢」を書き換える
const items = form.getItems();
for (const item of items) {
if (item.getTitle() === "注文数を選択してください") {
// プルダウン(asListItem) に型変換
const listItem = item.asListItem();
// 1 から newRemaining までの「文字列の配列」を作る
const choices = [];
for (let i = 1; i <= newRemaining; i++) {
choices.push(String(i)); // "1", "2", "3"... と文字列で入れていく
}
// プルダウンの選択肢を最新在庫に合わせて書き換え
listItem.setChoiceValues(choices);
// ヘルプテキスト(説明文)を更新
listItem.setHelpText("※現在の残り在庫は「あと " + newRemaining + " 個」です。");
break;
}
}
}
選択肢は文字列型のため、回答をそのまま取得すると文字列型としての数字になってしまい、残数の引き算ができなくなってしまいます。そのため、フォーム送信時の回答を受け付けるときに、注文数をこのように
const orderCount = Number(formData[3]);
Number()を使って数値型へ変換しています。
※前回と変更なし
動作確認
ラジオボタン方式
事前にフォームにラジオボタンを作っておきます。テストなので「1」だけでもOK。
プルダウン方式
こちらも事前にリストを作っておきます。テストなので「1」だけでもOK。
まとめ
今回は、GASを使ってラジオボタン/プルダウンリストを動的に変更する方法でした。
これで、ユーザーが残数以上の数量や3.5 のような小数を入力してしまうことを防ぐことができるようになりました。
今回紹介した方法は「注文数」のように選択肢が限られているケースでは非常に有効です。
ただし、どの質問形式が最適かは用途によって変わります。
| 方式 | 向いているケース | メリット | デメリット |
|---|---|---|---|
| 自由記述(短文) | 数量や金額など自由な数値入力 | 入力の自由度が高い | 入力ミス対策が必要 |
| ラジオボタン | 選択肢が少ない場合(10個程度まで) | 一覧で見やすい | 選択肢が多いと画面が長くなる |
| プルダウン | 選択肢が多い場合(10個以上) | 画面がすっきりする | 選択肢を開かないと内容が見えない |
今回のお弁当注文のように、注文可能数が10個程度で、在庫によって変化するケースでは、ラジオボタンやプルダウンを動的に変更する方法がおすすめです。
一方、注文可能数が数十個以上になる場合は、選択肢が多すぎて画面が見づらくなったり、目的の数字を探しづらくなったりします。そのような場合は、自由記述+回答の検証のほうが使いやすいこともあります。
用途に応じて使い分けてくださいね。
今回の成果コード
ラジオボタン方式
function onFormSubmit(e) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = e.range.getSheet(); //フォーム回答が入力されたシート
const row = e.range.getRow(); //フォーム回答が入力された行
const column = e.values.length; // 質問の列数を取得(イベントオブジェクトから取得)
const formData = sheet.getRange(row, 1, 1, column).getValues().flat();
const sheet_summary = ss.getSheetByName("集計シート");
// フォームの回答から「注文数」を数値化して取得
// formDataの中の4番目(インデックス [3] )を取得
const orderCount = Number(formData[3]);
// 「集計シート」のB1セルから現在の残数を読み込む
const currentRemaining = Number(sheet_summary.getRange("B1").getValue());
// 新しい残数を計算して、B1セルに上書き
const newRemaining = Number(currentRemaining) - orderCount;
sheet_summary.getRange("B1").setValue(newRemaining);
//集計シートに転記
sheet_summary.getRange(sheet_summary.getLastRow() + 1, 1, 1, column).setValues([formData]);
//紐づいているフォームを取得
const formUrl = ss.getFormUrl(); // スプレッドシートに紐づくフォームのURLを取得
const form = FormApp.openByUrl(formUrl);
// もし残数が0以下になったら、フォームを自動で締め切る
if (newRemaining <= 0) {
form.setCustomClosedFormMessage("本日の注文受付は【完売】のため終了いたしました。またのご利用をお待ちしております!");
form.setAcceptingResponses(false); // 新規受付を停止
} else { //残数がまだある場合
//フォーム全体の「説明欄」を書き換える場合
form.setDescription("【現在の残り在庫: " + newRemaining + " 個】");
//特定の質問(注文数)の「ラジオボタン選択肢」を書き換える
const items = form.getItems();
for (const item of items) {
if (item.getTitle() === "注文数を選択してください") {
// ラジオボタン(asMultipleChoiceItem)に型変換
const mcItem = item.asMultipleChoiceItem();
// 1 から newRemaining までの「文字列の配列」を作る
const choices = [];
for (let i = 1; i <= newRemaining; i++) {
choices.push(String(i)); // "1", "2", "3" ...と文字列で入れる
}
// ラジオボタンの選択肢を最新在庫に合わせて書き換え
mcItem.setChoiceValues(choices);
// ヘルプテキスト(説明文)を更新
mcItem.setHelpText("※現在の残り在庫は「あと " + newRemaining + " 個」です。");
break;
}
}
}
}
プルダウン方式
function onFormSubmit(e) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = e.range.getSheet(); //フォーム回答が入力されたシート
const row = e.range.getRow(); //フォーム回答が入力された行
const column = e.values.length; // 質問の列数を取得(イベントオブジェクトから取得)
const formData = sheet.getRange(row, 1, 1, column).getValues().flat();
const sheet_summary = ss.getSheetByName("集計シート");
// フォームの回答から「注文数」を数値化して取得
// formDataの中の4番目(インデックス [3] )を取得
const orderCount = Number(formData[3]);
// 「集計シート」のB1セルから現在の残数を読み込む
const currentRemaining = Number(sheet_summary.getRange("B1").getValue());
// 新しい残数を計算して、B1セルに上書き
const newRemaining = Number(currentRemaining) - orderCount;
sheet_summary.getRange("B1").setValue(newRemaining);
//集計シートに転記
sheet_summary.getRange(sheet_summary.getLastRow() + 1, 1, 1, column).setValues([formData]);
//紐づいているフォームを取得
const formUrl = ss.getFormUrl(); // スプレッドシートに紐づくフォームのURLを取得
const form = FormApp.openByUrl(formUrl);
// もし残数が0以下になったら、フォームを自動で締め切る
if (newRemaining <= 0) {
form.setCustomClosedFormMessage("本日の注文受付は【完売】のため終了いたしました。またのご利用をお待ちしております!");
form.setAcceptingResponses(false); // 新規受付を停止
} else { //残数がまだある場合
//フォーム全体の「説明欄」を書き換える場合
form.setDescription("【現在の残り在庫: " + newRemaining + " 個】");
//特定の質問(注文数)の「プルダウン選択肢」を書き換える
const items = form.getItems();
for (const item of items) {
if (item.getTitle() === "注文数を選択してください") {
// プルダウン(asListItem) に型変換
const listItem = item.asListItem();
// 1 から newRemaining までの「文字列の配列」を作る
const choices = [];
for (let i = 1; i <= newRemaining; i++) {
choices.push(String(i)); // "1", "2", "3"... と文字列で入れていく
}
// プルダウンの選択肢を最新在庫に合わせて書き換え
listItem.setChoiceValues(choices);
// ヘルプテキスト(説明文)を更新
listItem.setHelpText("※現在の残り在庫は「あと " + newRemaining + " 個」です。");
break;
}
}
}
}
さて、実務ではもう1つ重大なイベントが発生します。それは 人間(管理者)による手動での在庫変更 です。
「お弁当が売り切れそうになったから、急遽10個増枠」とか「管理者の在庫入力ミスを修正」といったケースでも、あわてず騒がずスマートにフォームに同期させたい。
次回はそんなニーズにお応えする、スプレッドシート側に設置する、管理者専用「フォーム同期ボタン」を作ってみましょう。






