1.はじめに
以前Webアプリから値を取得し、スプレットシートに保存する方法を学んだ。
GAS勉強会2 ~Webアプリからスプレットシートに値を登録する~
今回はより実用的な方法としてWebアプリに複数の入力欄を作成し、新たなデータとしてスプレットシートに保存する方法を学ぶ。
ついでに、今後データの編集も行うことを想定し、データごとに固有IDとタイムスタンプを付与する。
2. GAS、HTMLの骨格
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
</script>
</head>
<body>
<h2>商品登録フォーム</h2>
<form id="product_form">
<label for="product_name">商品名:</label>
<input type="text" id="product_name" required><br><br>
<label for="price">価格:</label>
<input type="number" id="price" required><br><br>
<button type="button" onclick="submitForm()">登録</button>
</form>
</body>
</html>
GAS
const ss = SpreadsheetApp.openById("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
const sheet = ss.getSheetByName("シート1");
function doGet() {
return HtmlService.createHtmlOutputFromFile("index");
}
今回は「商品名」と「価格」を入力し登録することにする。
3.GASのプログラムを完成させる
GASのプログラムに以下の関数を追加する。
function addProduct(products) {
const lastRow = sheet.getLastRow();
// IDの一覧を作る。Set で作ると処理早くなるらしい。
// Math.maxを使うことで0行目を参照するリスクをなくす。
const existingIds = new Set(sheet.getRange(2, 1, Math.max(1, lastRow - 1), 1).getValues().flat());
// console.log(Array.from(existingIds));
const newId = generateUniqueId(existingIds);
const timestamp = new Date().getTime();
sheet.appendRow([newId, timestamp, products.productName, products.price]);
return "success";
}
function generateUniqueId(existingIds) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let id;
do {
id = Array.from({ length: 8 }, () =>
chars[Math.floor(Math.random() * chars.length)]
).join('');
} while (existingIds.has(id)); //重複してたら再生成
return id;
}
addProducts
関数でHTMLから値を取得し、スプレットシートに保存する仕組みを作っている。
existingIds
に既に作成されているID一覧を保存する。Math.max(1, lastRow - 1)
とすることでスプレットシートにデータがない場合に0行目(存在しない)の値を取得しようとすることを防ぐ。
new Set
とすることで処理が速くなるらしい。知らんけど。
generateUniqueId(existingIds)
で新しいIDを作成する。この関数の中身は後で説明する。
new Date().getTime()
で現在時刻を取得する。.getTime()
にすることでタイムスタンプにする。new Date()
のデータのままではブラウザに日付データを送るときにエラー吐くのでタイムスタンプに変換している。
sheet.appendRow([newId, timestamp, products.productName, products.price]);
ID、タイムスタンプを含めた値を保存する。
※HTMLからはオブジェクト形式でデータを送られていることに注意。
function generateUniqueId(existingIds) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let id;
do {
id = Array.from({ length: 8 }, () =>
chars[Math.floor(Math.random() * chars.length)]
).join('');
} while (existingIds.has(id)); //重複してたら再生成
return id;
}
ここの関数では新たなIDを作成するが、条件として
1.英数字36文字からランダムに8桁のIDを作成する。
2.過去に既に使用されているIDであれば新規IDを作り直す。
としている。
do{}
の中身が実際のプログラム部分。
Array.from
ではまず長さ8の空の配列を作り、() => chars[Math.floor(Math.random() * chars.length)]
で各要素ごとにランダムな文字列を選ぶ。
chars.length
⇒ 36
Math.random() * 36
⇒ 0~35の乱数を生み出す。
Math.floor
で小数点以下切り捨て。
つまり、
chars[Math.floor(Math.random() * chars.length)]
は[]内で0~35のランダムな整数を生成し、生成された値に該当する文字をchars
から取得している。
.join('')
で配列を文字列に連結する。
(例:["A", "B", "3", "X", "Z", "9", "T", "1"]
→ "AB3XZ9T1"
while (existingIds.has(id));
4.HTMLの処理を完成させる
HTMLの<script>
に以下を記述する。
// フォームの送信処理
function submitForm() {
const productName = document.getElementById("product_name").value;
const price = document.getElementById("price").value;
if (!productName || !price) {
alert("必須項目を入力してください。");
return;
}
google.script.run
.withSuccessHandler(() => {
alert("商品を登録しました>");
document.getElementById(product_form).reset();
})
.withFailureHandler((err) => {
alert("エラー:" + err.message);
})
.addProduct({ productName, price });
}
// inoputに対するEnterキー操作を監視
window.onload = function () {
document.querySelectorAll("input").forEach(input => {
input.addEventListener("keydown", preventEnterSubmit);
});
};
// Enterキーで送信させない処理
function preventEnterSubmit(event) {
if (event.key === "Enter") {
event.preventDefault(); // デフォルトの送信動作を無効化
const inputs = Array.from(document.querySelectorAll("input"));
const index = inputs.indexOf(event.target);
if (index > -1 && index < inputs.length - 1) {
inputs[index + 1].focus(); //次の入力欄へ移動
}
}
}
submitForm()
の説明
productName
、price
変数を定義して入力された値を保存する。
if文で空白の入力欄があればエラーを吐くようにする。
google.script.run
でサーバーにデータを送り結果の処理をする。
.addProduct({ productName, price });
としてオブジェクト型でサーバーへ送る。
おまけのプログラム
window.onload = function () {
とpreventEnterSubmit(event)
関数はおまけとして作ってみた。
上の入力欄を記入し終わると、エンターキーを押したら次の入力欄に行くようにしている。
ただし、最後の入力欄に記入した後はエンターキーを押しても何も起こらず、ボタンを押した場合のみ送信処理が行われるように調整している。
document.querySelectorAll("input").forEach(input => {
ですべての入力欄を監視する。
input.addEventListener("keydown", preventEnterSubmit);
では入力欄で何かキーが押されたらpreventEnterSubmit
関数を呼び出している。
if (event.key === "Enter") {
で押されたキーがエンターキーの場合のみを扱うようにしている。
event.preventDefault();
はデフォルトではエンターキー入力で送信処理が行われてしまうのを夢告化している。
Array.from(document.querySelectorAll("input"));
は<input>
要素を配列に変換末う。
inputs.indexOf(event.target);
では<inout>
要素の中の何番目でエンターキーが押されたか確認する。
if (index > -1 && index < inputs.length - 1) {
inputs[index + 1].focus(); //次の入力欄へ移動
では一番下の入力欄でエンターキーを押されたかを確認し、一番下の入力欄でない限り次の入力欄へ移動する処理をしている。
5.まとめ
以上で複数のデータをスプレットシートに登録することができた。(なんか話がすごく脱線していたような…)