概要
- Google Apps Scriptsのウェブアプリケーション
- スプレッドシートの参照、更新、メール送信ができるため、現場でちょっとしたツールを作るのに向いてる
- gasでウェブアプリケーションとして導入するまでの手順は省略
- 使いまわせるような雛形を作っておき、後でコピペで作れるようにしておくことを目的としてます
リンク
- 公式ガイド:https://developers.google.com/apps-script/guides/web
- gasサンプル集:https://github.com/google/google-apps-script-samples
サンプルプログラムの内容
- 入力フォームの内容をスプレッドシートに追記する
- 入力フォームのセレクトボックス(動的)は、マスタデータを記載しているスプレッドシートから動的に生成する
使用ライブラリ
- jquery(あまり使いたくないけど、以下のライブラリ使うのに必要なため)
- bootstrap(デザイン)
- fontawesome(フォント)
- toastr(トースト通知)
- bootstrap-select(高機能セレクトボックス)
- bootstrap-validator(入力チェック)
ソース
-
コード.js
:GETリクエストきたときにindex.html表示。スプレッドシート参照・更新処理。 -
index.html
:入力フォーム画面のhtmlを記述。 -
javascript.html
:javascriptを記述。index.htmlから外だししておく。 -
stylesheet.html
:スタイルシートを記述。index.htmlから外だししておく。
コード.js
- doGet():GETリクエストがきたら、index.htmlを返す。javascriptとスタイルシートを外だしするために、
HtmlService.createTemplateFromFile()
を使用する。
- registerSSByFormData():入力フォームデータを受け取って、スプレッドシートを更新する。 - getSelectListFromMasterSS():スプレッドシートを参照し、入力フォームのセレクトボックスを動的に作成する。
function doGet() {
return HtmlService.createTemplateFromFile('index')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.addMetaTag('viewport', 'width=device-width, initial-scale=1')
.setTitle('gas入力フォームサンプル');
}
function registerSSByFormData(data) {
Logger.log("data = %s", data);
var datasheet = SpreadsheetApp.openById('送信データを登録するスプレッドシートのID').getSheetByName('シート名');
var now = new Date();
var i = datasheet.getLastRow() + 1;
datasheet.getRange(i, 1).setValue(data[ 1]);
datasheet.getRange(i, 2).setValue(data[ 2]);
datasheet.getRange(i, 3).setValue(data[ 3]);
datasheet.getRange(i, 4).setValue(data[ 4]);
datasheet.getRange(i, 5).setValue(data[ 5]);
datasheet.getRange(i, 6).setValue(data[ 6]);
datasheet.getRange(i, 7).setValue(data[ 7]);
datasheet.getRange(i, 8).setValue(data[ 8]);
datasheet.getRange(i, 9).setValue(data[ 9]);
datasheet.getRange(i, 10).setValue(data[10]);
datasheet.getRange(i, 11).setValue(data[11]);
datasheet.getRange(i, 12).setValue(Utilities.formatDate(now, 'Asia/Tokyo', 'yyyy/MM/dd HH:mm:ss'));
result = true;
return {data: true};
}
function getSelectListFromMasterSS() {
var selectList = [];
// マスタデータシートを取得
var datasheet = SpreadsheetApp.openById('マスタデータのスプレッドシートのID').getSheetByName('シート名');
// B列2行目のデータからB列の最終行までを取得
var lastRow = datasheet.getRange("B:B").getValues().filter(String).length - 1;
Logger.log("lastRow = %s", lastRow);
// B列2行目のデータからB列の最終行までを1列だけ取得
selectList = datasheet.getRange(2, 2, lastRow, 1).getValues();
Logger.log("selectList = %s", selectList);
return {data: selectList};
}
index.html
-
HtmlService.createHtmlOutputFromFile()
を使い、外だししてあるjavascript.htmlと、stylesheet.htmlをincludeする。 - デザインにはbootstrapを使用
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<?!= HtmlService.createHtmlOutputFromFile('stylesheet').getContent(); ?>
</head>
<body>
<nav class="navbar navbar-inverse">
<div class="navbar-header"><a class="navbar-brand" href="#">入力フォーム</a></div>
</nav>
<div class="container">
<form class="form-horizontal" id="myForm" onsubmit="return false;">
<div class="form-group">
<label class="control-label col-xs-3" for="input_id1">氏名</label>
<div class="col-xs-9">
<input type="text" class="form-control" id="input_id1" required>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-3" for="input_id2">パスワード</label>
<div class="col-xs-9">
<input type="password" class="form-control" id="input_id2" required>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-3">チェックボックス</label>
<div class="col-xs-9">
<div class="checkbox" id="checkbox1">
<label><input type="checkbox" id="input_id3" >check1</label>
<label><input type="checkbox" id="input_id4" >check2</label>
<label><input type="checkbox" id="input_id5" >check3</label>
</div>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-3">ラジオボタン</label>
<div class="col-xs-9">
<div class="radio" id="radiobutton1">
<label><input type="radio" name="radio1" id="input_id6" required>radio11</label>
<label><input type="radio" name="radio1" id="input_id7" required>radio12</label>
<label><input type="radio" name="radio1" id="input_id8" required>radio13</label>
</div>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-3">セレクトボックス(search)</label>
<div class="col-xs-9">
<select id="select_id1" class="selectpicker form-control" data-live-search="true" width="auto">
<option selected>選択肢1</option>
<option>選択肢2</option>
<option>選択肢3</option>
<option>テストA</option>
<option>テストB</option>
</select>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-3">セレクトボックス(multiple)</label>
<div class="col-xs-9">
<select id="select_id2" class="selectpicker form-control" multiple >
<option selected>ガリガリ君(ソーダ味)</option>
<option>ハーゲンダッツ</option>
<option>ピノ</option>
<option>ガリガリ君(コーラ味)</option>
</select>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-3">セレクトボックス(動的)</label>
<div class="col-xs-9">
<select class="form-control" id="select_id3"></select>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="form-group">
<div class="col-xs-offset-3 col-xs-9">
<button id="send_button" type="submit" class="btn btn-primary btn-large">
<i class="fa fa-send fa-lg"></i>送信
</button>
</div>
</div>
</form>
<i id="processing" class="fa fa-refresh fa-spin fa-3x fa-fw"></i>
</div>
<?!= HtmlService.createHtmlOutputFromFile('javascript').getContent(); ?>
</body>
</html>
stylesheet.html
- CDNのスタイルシートを参照
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.12.1/css/bootstrap-select.min.css">
javascript.html
- 処理内容はコメントに記載の通り。
- コード.gs側のfunctionを呼び出しするのに
google.script.run.withSuccessHandler
を使用します。 - bootstrap-validatorのバリデーションはsubmitしないとかからないため小細工してます
- フォームsubmitで、submit用のボタンのスタイルがdisabledだったら、バリデーションエラーなので、送信処理はしないようにする
- フォームsubmitで、画面遷移させないように、
onsubmit="return false;"
にしておく
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script src="https://use.fontawesome.com/7bcbed1321.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.12.1/js/bootstrap-select.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/1000hz-bootstrap-validator/0.11.8/validator.min.js"></script>
<script>
/**
コード.jsのgetSelectListFromSS()を読んで、スプレッドシートのデータをもとに、セレクトボックスを構築する
*/
function getSelectList() {
try {
google.script.run
.withSuccessHandler(function(result) {
console.log("result", result);
var select = document.getElementById('select_id3');
var option = document.createElement('option');
option.setAttribute('value', '');
option.setAttribute('selected', true);
option.innerHTML = '未選択';
select.appendChild(option);
for (var i = 0; i < result.data.length; i++) {
option = document.createElement('option');
option.setAttribute('value', i + 1);
option.innerHTML = result.data[i];
select.appendChild(option);
}
})
.withFailureHandler(function(result) {
toastr.error('リストの取得に失敗しました。', result);
})
.getSelectListFromMasterSS();
} catch (e) {
toastr.error(e);
}
}
/**
コード.jsのregisterSSByFormData()を呼んで、フォームの内容をスプレッドシートに登録する
*/
function sendData() {
try {
processing(true);
var elemntIFrame = window.parent.document.getElementById('userHtmlFrame');
elemntIFrame.setAttribute('scrolling', 'no');
elemntIFrame.setAttribute('frameborder', '0');
console.log("document.getElementById('select_id2').selectedIndex)", document.getElementById('select_id2').selectedIndex);
var data = [];
data[0] = 'dummy';
data[1] = document.getElementById('input_id1').value;
data[2] = document.getElementById('input_id2').value;
data[3] = document.getElementById('input_id3').checked;
data[4] = document.getElementById('input_id4').checked;
data[5] = document.getElementById('input_id5').checked;
data[6] = document.getElementById('input_id6').checked;
data[7] = document.getElementById('input_id7').checked;
data[8] = document.getElementById('input_id8').checked;
data[9] = document.getElementById('select_id1').options[document.getElementById('select_id1').selectedIndex].text;
data[10] = getMultipleSelectedText(document.getElementById('select_id2').options);
data[11] = document.getElementById('select_id3').options[document.getElementById('select_id3').selectedIndex].text;
console.log("data", data);
google.script.run
.withSuccessHandler(function(result) {
processing(false);
if (result.data) {
toastr.info('送信完了');
} else {
toastr.error('送信失敗しました');
}
})
.withFailureHandler(function(result) {
processing(false);
toastr.error('データ送信に失敗しました。', result);
})
.registerSSByFormData(data);
} catch (e) {
processing(false);
toastr.error(e);
}
}
// multipeセレクトをカンマ区切りのテキストにして取得
function getMultipleSelectedText(selectOptions){
console.log(selectOptions, selectOptions.length);
var arr = [];
for(var i = 0; i < selectOptions.length; i++) {
if(selectOptions[i].selected){
arr.push(selectOptions[i].value);
}
}
return arr.join(',');
}
// 処理中アイコン表示・非表示
function processing(processing) {
if (processing) {
document.getElementById('processing').style.visibility = "visible";
document.getElementById('send_button').setAttribute("disabled", true);
} else {
document.getElementById('processing').style.visibility = "hidden";
document.getElementById('send_button').removeAttribute("disabled");
}
}
// ロード完了後の処理
$(function() {
// トースト通知の位置指定
toastr.options.positionClass = "toast-bottom-left";
// バリデーションチェック有効化
$('#myForm').validator();
// 処理中アイコン非表示
processing(false);
// セレクトボックスのリストを動的に取得する
getSelectList();
// submit時のイベント登録
$('#myForm').validator().on('submit', function(e) {
if (document.getElementById('send_button').className.indexOf("disabled") != -1) {
// submit用のボタンのスタイルにdisabledが設定されていればバリデーションエラー
} else {
// バリデーションエラーでなければ送信
sendData();
}
})
});
</script>