google apps scriptで簡単なwebアプリを作ってみた
googlespreadsheet(つまりいわゆるエクセル的な)にデータベース様のテーブルを作っておき、このシートを開けないスマホやマック環境でも、HTMLフォームから更新できるようにする。
tomatojuicer222.hatenablog.com
・おおざっぱな流れは以下。
シート上にテーブルのひな形を作成する。
ここではレコードが増えた場合も表示をスムーズにするため、入力規則やセル内の計算式を使用しない。つまり手動ならなんでも書けてしまう様なシートを作っておく。
代わりにHTMLのフォーム側で値チェック等を行い、検索に差し支えるような誤入力を抑止する設計とする。
サーバー側のjavascript処理をコーディング/フォーム側のHTML(CSS,javascript含む)で画面作成し、大枠が完成したらプロジェクトをWEB上に公開する。
公開したURLにアクセスすると、サーバー上ではdoGet()メソッドが呼ばれる。
このdoGet()メソッドに、作成したHTMLで描画するコードをHTMLserviceクラスを使って書いておく。
HTMLからの応答方法としては、html側でinput type="button"のonClickに、google.script.runすればよい。
ここまではググればすぐでてくるので詳細は割愛。
・以降あんまりググっても書いてなかった、どハマりした点メモ。
・引数に設定できる値が限られているので注意。
google.script.runで呼び出す際の引数は種類が限られている。基本はまぁformを渡すんだけど、この場合HTMLのformオブジェクトがそのまま渡るわけではなく、formの各要素の値がneme:valueのマップ形式で通知されてるっぽい。
そのためbuttonなど(一般的なsubmitボタンで渡る)一部の項目についてはvalueがとれないので要注意。
たとえば押したボタンによって参照するデータを区別する、みたいなことをする場合、次に書く通りブラウザ側で処理してから渡さないとダメ。
・複数formの取扱い/input要素の取扱い。
複数フォームを用意して、押したボタンによってどちらかのフォームか判定、送信用の抽象フォームに移し替える、みたいな処理。
ググると結構でてくるからメジャーなやり方なんだろうけど、onclick処理に入った時点で各formの値にアクセスする方法は結構嘘ばっかり。まともに通るサンプルソースに出会えなかった。
具体的にはサンプルの「document.formhidden」の時点では認識できてるんだけど、その先の.nameからはブラウザによって動いたり動かなかったり。
<!--うまく動かないダメな例です!!!-->
<!--送信に使う抽象フォーム-->
<form name="formhidden">
<input type="hidden" name="radio" value="">
<input type="hidden" name="rank" value="">
</form>
<!--記入用フォーム1-->
<form name="forminput1">
<input type="radio" name="r1" value="1">
<input type="text" name="rank1" >
<input type="button" name="b1" onclick="sendForm1(this.parentNode)">
</form>
<!-記入用フォーム2-->
<form name="forminput2">
<input type="radio" name="r2" value="2">
<input type="text" name="rank2" >
<input type="button" name="b2" onclick="sendForm2(this.parentNode)">
</form>
<script>
function sendForm1(form){ //フォーム1からの送信
document.formhidden.radio = form.r1;
sendForm(document.formhidden);
}
function sendForm2(form){ //フォーム2からの送信
document.formhidden.radio = form.r2;
sendForm(document.formhidden);
}
sendForm(form){
//サーバーに送信
google.script.run.send(form);
}
</script>
<!--うまく動かないダメな例ここまで-->
これをまともに動かすは、idとnameの違いをきちんと理解する必要がある。
- id:全要素で重複があってはいけない
- name:inputのtype="radio"に代表されるように、重複がある前提
つまりnameは受け取ったサーバー側でvalueを取るためのものであり、本来要素の特定に使うものではない!
「document.name」で値をとってくること自体が(十分に注意して設計した場合に)一部ブラウザのみで使えるおまけ機能であって、基本的にNGと思っとけばよさそう?
個別に特定可能な要素には全てidを振っておき「document.getElementById("id")」で取得する。
ラジオボタンみたいなnameまでしか特定できないものは「document.getElementsByName("name")」で一度同じnameグループの要素リストを取得する。
このリストには配列の添え字でアクセス可能なので、具体的には以下の例のようにやれば選択した(checkedな)値をとることができる。
var i;
var selectedval;
var elements = document.getElementsByName("name");
for (i=0; i<elements.length; i++){
if(elements[i].checked){
selectedval = elements[i].value;
}
}
このへんをちゃんとやらずに.nameでアクセスしようとすると、
1.配列の先頭が取れる
2.undefinedな値でエラーになる(これがHTML標準なのかな?)
3.選択したラジオボタンのvalueがとれる(一部ブラウザのみのおまけ機能、一見ただしく動いて見える)
あたりが環境によってまちまちに動く。このへんが冒頭の不具合の原因。
同じ理屈はselectにもいえて、やっぱりID指定や.nameでselect要素自体を指定しても、配下option要素のvalueはとれない。
ここもselect要素をidで取得したら、「.options」プロパティで配下optionリストを取得する。
このリストには配列の添え字でアクセス可能かはよくわからん。ちょっと複雑だが以下。
何番目のoptionが選択されたかという情報を「.selectedIndex」プロパティで取得し、
options側で持ってるアクセサメソッド「.item()」にこのselectedIndexを指定する。
これで選択した(selectedな)option要素の値をとることができる。
var select = document.getElementById("selectid");
var options = document.getElementById("selectid").options;
var selectedvalue = options.item(select.selectedIndex).value;
・spreadSheetAppのスプレッドシート特定方法
スプレッドシートを操作する方法はググればでてくるので割愛。
ただしwebアプリ的に使う場合、シートと連携しているGASであってもactivecheetではとれない。
openByIdみたいなメソッドを使って明示的に開く必要がある。
ここでいうIDはシートのURLのうち、aaa/d/XXXXXX/aaa のXXXXXX部分。
とりあえずグローバル変数に固定値で保持しておく。
動的に指定のシートを取れるよなうまい取り方がないものか模索中。(公開することを想定して)
・その他
この辺のうまくいかなかった点は、ググってもわからなかった項目で、何度も試行を繰り返すなかでなんとか解決してきた。
そんなことをやってたら「一秒当たりのスクリプト実行回数が多すぎます」みたいなエラーがでたよ。
おそらくこの24時間で500~1000くらいの回数実行してるから、その辺のどっかに実行回数の制限ポイントがあるんだろうな。ググっても出てこなかったけど。