どうも。つじけ(tsujikenzo)です。このシリーズでは 「Googleフォームとイベントオブジェクトを極めよう」 について全6回でお送りします。今日は5回目です。
前回のおさらい
前回は、 「スプレッドシートのコンテナバインドのフォーム送信時トリガー」 ということで、 主に 「多(フォーム)対1(シート)の回答集計とGAS」 についてお届けしました。
今回は、 「フォームのコンテナバインドのフォーム送信時トリガー」 をお届けしします。
アジェンダ
1. フォームのコンテナバインドのフォーム送信時イベントオブジェクト
2. スプレッドシートの名前付き範囲
3. 名前付き範囲に、フォームから入力する(1対1)
4. フォーム送信時にスプレッドシートへ入力する(1対多)
フォームのコンテナバインドのフォーム送信時イベントオブジェクト
公式リファレンスによると、フォームのコンテナバインドのフォーム送信時に渡されるイベントオブジェクトには、下記の 4つのプロパティ が格納されています。
このイベントオブジェクトに格納されているプロパティを操作することで、フォームの [回答] を自由自在に操ることができます。
自由自在に操る例として、 「フォームから納品書スプレッドシートへの入力ツール」 を作成しますが、まず スプレッドシートの名前付き範囲 をご紹介します。
スプレッドシートの名前付き範囲
スプレッドシートの単体セル、および複数セル範囲には、 「名前」 を付けることができます。 名前付き範囲 については、過去にブログを書きましたので、こちらの記事を参考にしてください。
今回は、このようなスプレッドシートを用意しました。シート [じゃがいも] には、セル[E3]に [送信日] 、セル[B11]に [じゃがいも数量] という、2つの 名前付き範囲 が設定されています。
この2つの名前付き範囲の値を、 フォームから更新 しましょう。
名前付き範囲に、フォームから値を入力する(1対1)
新規フォームを作成します。タイトルはなんでも構いませんが、今回は 「納品書入力用フォーム」 としました。
2つの質問を設置します。
1問目の質問は 「送信日を入力してください」 とし、 [日付] 、 [必須] を設定しました。
2問目の質問は 「じゃがいも数量を入力してください」 とし、[記述式] 、 [数値] 、 [必須] を設定しました。
なお、数値を入力してもらうために、 「次より大きい」 に 0 を、 「カスタムのエラーテキスト」 に 「0より大きい数値を入力してください」 を入力しています。
この設定(バリデーションチェックと呼ばれます)は、 [縦3点メニュー] をクリックして、 [回答の検証] をONにすると可能になります。
テスト回答を1件送信してみましょう。
フォーム編集画面に戻ると、 [回答] タブには1件の回答が表示されています。
フォームの作成は以上です。続いてGASを書きます。
フォームのコンテナバインドスクリプトとイベントオブジェクト
このフォームの、 コンテナバインドスクリプト にGASを書きます。右上の [縦3点メニュー] から、 <>スクリプトエディタ をクリックします。
[タイトル] は、フォームと同じがわかりやすいでしょう。
フォームのコンテナバインドのフォーム送信時に渡されるイベントオブジェクトに、 [response] プロパティがありました。中には [FormResponseオブジェクト] が格納されています。
response プロパティを呼び出すと、 FormResponseオブジェクト を取得することができるということです。
console.log(e.response); //{} FormResponseオブジェクト
FormResponseオブジェクトとは
フォームの [回答] は、 FormResponseオブジェクト と呼ばれる、回答に関するあらゆるデータを集めたかたまりになっています。
先ほど、テスト回答を送信した際、[回答] タブには1件の回答が表示されていましたが、サーバーには [FormResponseオブジェクト] が生成されていることになります。
フォームのコンテナバインドスクリプトでは、送信後の回答を いつでも、 [FormResponseオブジェクト] として取得できます。
function getItemResponsesText() {
const form = FormApp.getActiveForm();
const responses = form.getResponses(); //[{},{},{}] 複数または単体のFormResponseオブジェクト
}
[Class FormResponse] には、[getItemResponses()メソッド] が用意されています。これは、フォームの回答である FormResponseオブジェクト から、すべての ItemResponseオブジェクト を順番どおりに配列で取得します。
FormResponseオブジェクト.getItemResponses()
さらに、[Class ItemResponse] には、[getResponse()メソッド] が用意されており、ItemResponseオブジェクト から回答の中身(文字列として)を返すことができます。※公式リファレンスにもありますが、正式には戻り値はオブジェクトですが、多くの場合は文字列を返すようです。
ItemResponseオブジェクト.getResponse()
一連の流れをコード化したものがこちらです。
function getItemResponsesText() {
const form = FormApp.getActiveForm();
const responses = form.getResponses(); //[{},{},{}] 複数または単体のFormResponseオブジェクト
const itemResponses = responses[0].getItemResponses();
const itemResponsesText = itemResponses.map(itemResponse => itemResponse.getResponse());
console.log(itemResponsesText);//[ '2021-04-26', '10' ]
}
同様に、 フォームの送信時 でも [FormResponseオブジェクト] を取得できます。
前項で紹介したコードがまさに、 フォームの送信時にFormResponseオブジェクトを取得するコード です。
function onFormSubmit(e) {
const formResponseObject = e.response; //{} FormResponseオブジェクト
}
繰り返しになりますが、FormResponseオブジェクトは、 getItemResponses()メソッド や、 getResponse()メソッド により、 [回答] をテキストに変換できます。
一連の流れをコード化したものがこちらです。
function onFormSubmit(e) {
const formResponseObject = e.response; //{} FormResponseオブジェクト
const itemResponses = formResponseObject.getItemResponses();
const itemResponsesText = itemResponses.map(itemResponse => itemResponse.getResponse());
console.log(itemResponsesText);
}
トリガーの設置
トリガーを設置して確認してみましょう。
テスト送信をします。
スクリプトエディタの [実行数] を開きます。
[回答] がログ出力されています。
納品書スプレッドシートに値を入力する
さきほどのコードに加筆し、 回答 をスプレッドシートに入力するコードを書きます。
function onFormSubmit(e) {
const formResponseObject = e.response; //{} FormResponseオブジェクト
const itemResponses = formResponseObject.getItemResponses();
const itemResponsesText = itemResponses.map(itemResponse => itemResponse.getResponse());
console.log(itemResponsesText); //[ '2021-04-27', '55' ]
//スプレッドシートへ出力
const SSID = 'スプレッドシートID';
const ss = SpreadsheetApp.openById(SSID);
ss.getRange('送信日').setValue(itemResponsesText[0]);
ss.getRange('じゃがいも数量').setValue(itemResponsesText[1]);
}
スプレッドシートへ出力できました。
これは、言わばフォームとスプレッドシートが 1(フォーム)対1(スプレッドシート) の関係であると言えます。
このフォームとコンテナバインドスクリプトを発展させると、 1(フォーム)対多(スプレッドシート) のツールとして応用できます。
フォーム送信時にスプレッドシートへ入力する(1対多)
作成するツールの手順は以下のようになります。
– 名前付き範囲に汎用性をもたせる
– フォームの「質問1」に、スプレッドシートIDを用意する
– イベントオブジェクトから回答を取得する
– スプレッドシートIDを取得して掴む
– スプレッドシートにsetValue()する
それでは、1つずつみていきましょう。
名前付き範囲に汎用性をもたせる
[名前付き範囲] の汎用性が高くなるように、[名前] を変更します。たとえば、「送信日」だったものを [値1] とし、「じゃがいも数量」だったものを [値2] に変更します。
名前付き範囲は、スプレッドシート全体を通して、 「ユニークな値」 である必要があります。重複した名前を付けることができません。運用方法としては、 [value1, value2, …value n] のように シート順 、 セルの左上から順 に連番をつけるとわかりやすいかもしれません。
「じゃがいも」とは別の納品書を作成するために、「にんじん納品書」をスプレッドシートのコピーで作成しました。 名前付き範囲 を確認すると、じゃがいも納品書と同じ 名前付き範囲 が設定されています。
にんじん納品書には、「納品先」のセル[E11]に、名前付き範囲 [値3] を追加しました。
フォームの「質問1」に、スプレッドシートIDを用意する
フォームを作成します。前回は「納品書入力用フォーム」でしたが、今回は 「スプレッドシート入力用」 というフォーム名をつけます。
まず、完成系のフォームをご覧ください。
1問目 の質問には、「分かりやすいスプレッドシート名と、スプレッドシートID」 をカンマ区切りで設定しています。
スプレッドシート名, スプレッドシートID
選択方式はなんでも構いませんが、今回は [プルダウンメニュー] にしております。
値1~3はそれぞれ [記述式テキスト(短文回答)] で設定します。今回は3つ用意しましたが、必要な数だけ増やして(値10~など)構いません。
イベントオブジェクトから回答を取得する
コンテナバインドスクリプトを開きます。
フォームと同じプロジェクト名を設定します。
復習ですが、フォームのコンテナバインドスクリプト の、フォーム送信時イベントオブジェクトから回答を取得する方法は、こちらでした。
//イベントオブジェクトから回答を配列に格納する
const itemResponses = e.response.getItemResponses();
const responseArray = itemResponses.map(itemResponse => itemResponse.getResponse());
スプレッドシートIDを取得して掴む
[回答] の先頭には、「スプレッドシート名, スプレッドシートID」 が格納されています。replace()メソッドを使って、 スプレッドシートID を取得できます。
さらに、スプレッドシートIDを使って、スプレッドシートを掴むことができます。
//スプレッドシートIDを取得して掴む
const id = responseArray[0].replace(/.*,/, '');
const ss = SpreadsheetApp.openById(id);
スプレッドシートにsetValue()する
名前付き範囲は、[Class SpreadSheet] のメンバーですので、範囲を取得して、setValues()ができます。
スプレッドシートオブジェクト.getRange(シート名!名前付き範囲).setValue()
回答の配列を、名前付き範囲の 「値 + No」 に、for文で1つずつsetValue()するコードはこのようになります。
//値1~値nを配列に格納する
responseArray.splice(0, 1);
//スプレッドシートに貼り付け
for (const [index, response] of responseArray.entries()) {
ss.getRange(`値${index + 1}`).setValue(response);
}
完成したコードとツール
上記をまとめたコードがこちらです。
/**
* フォームのコンテナバインドスクリプトからスプレッドシートに入力する関数
*
* @param {object} イベントオブジェクト
* @return none
*
*時限トリガー:フォーム送信時
*/
function setValueToSheet(e) {
//イベントオブジェクトから回答を配列に格納する
const itemResponses = e.response.getItemResponses();
const responseArray = itemResponses.map(itemResponse => itemResponse.getResponse());
//スプレッドシートを取得して掴む
const id = responseArray[0].replace(/.*,/, '');
const ss = SpreadsheetApp.openById(id);
//値1~値nを配列に格納する
responseArray.splice(0, 1);
//スプレッドシートに貼り付け
for (const [index, response] of responseArray.entries()) {
ss.getRange(`値${index + 1}`).setValue(response);
}
}
コンテナバインドスクリプトに、 送信時トリガー を設定します。
トリガーが追加されました。
スプレッドシート入力用フォーム から、テスト回答を送信してみましょう。
スプレッドシートが変更されました。
それでは、同じフォームから、別のスプレッドシートに入力をしてみましょう。
「じゃがいも納品書」 には名前付き範囲 [値1] と [値2] を設定していました。
スプレッドシート入力用フォームには、[スプレッドシート名, スプレッドシートID] が必要ですので、フォームの [質問1] に追加します。
テスト回答を送信してみましょう。
「じゃがいも納品書」の [名前付き範囲] を変更できました。
まとめ
以上で、 「フォームのコンテナバインドのフォーム送信時トリガー」 ということで、、[フォームのコンテナバインドのフォーム送信時イベントオブジェクト] を確認し、[スプレッドシートの名前付き範囲] を復習しました。
後半は、[名前付き範囲に、フォームから入力する(1対1)] コードを書き、最後は [フォーム送信時にスプレッドシートへ入力する(1対多)] のツールを完成させました。
データを入力するひな形が多い 、増え続けるUI(ユーザーインターフェイス)をまとめたい という方のお役に立てば幸いです。
次回は最終回で、「トリガー作成アカウントと実行アカウントの違いについて」 をお届けします。