UiAppは、GoogleがJava用に提供しているGWTを、Google Apps Scriptから利用する為のクラスです。HTML Servicesと対をなすGUI構築の為のクラスですが、HTML Servicesよりも、やや自由度が高目で、リッチなUIを実現することが可能です。但し、HTML ServicesがHTML + CSS + JavaScriptで構成されていて、資料も結構あるのに対して、UiAppは広く使われてはおらず、資料も少ないので、正直な所あまりおすすめできる状況にありません。
HTML Services同様に、doGet()で起動して、表示するので、例えばダイアログやサイドバー、ウェブアプリケーションを作るなど、用途はHTML Servicesと変わりません。なお、この項目は既に作成している「色々なメッセージボックスを使いたい」や「ファイルやフォルダの選択画面を作りたい」、「検索窓と結果の返り値」にもつながる項目です。
UiAppは、HTML Servicesとは異なり、Google Apps Script上でGUIの部品となるモノ、ハンドラー、返り値を処理するものなどを全て実装するもので、少々難解な仕組みになっています。しかし、一方でGWTの各種コンポーネントをGUI部品として使える為、HTML Servicesで制限が多い中がんばって作るよりも、リッチなUIを実現できるので、よりアプリケーションよりのサービスを作りたいとなると、これしか選択肢がありません。但し、見た目はそれほど変えられないので、GUIがダサいと言ってしまうとそれまでになってしまいます。
//単なるボタンを表示するだけのスクリプト
function doGet(){
var app = UiApp.createApplication();
var button = app.createButton("送信");
app.add(button);
return app;
}
そのまま、部品を次々にcreateして、作成したappに.addしていってもいいのですが、あまりにも無骨過ぎます。なので、通常は、ある程度の部品群をまとめる部品を使ってレイアウトを組むことになります。以前はこの当たりは、GUI Builderというツールがあって、マウスでポチポチ作れたのですが、現在はなくなっています。その為、頭の中でイマジネーションを働かせて、このまとめる部品にボタンやテキストボックスなどを配置することになります。ちなみに、このまとめる部品のことを「パネル」と呼び、パネルを使わない場合、addされた部品は、次々に縦にそのまんま並ぶだけになります。
このパネルも何種類か存在しており、自分が創りあげたいインターフェースに応じて、そのパネルも選ぶことになります。
function doGet(e){
var app = UiApp.createApplication().setTitle("テストだよ");
var lb1 = app.createLabel("パネルを使ったサンプルのページ");
app.add(lb1);
var panel = app.createHorizontalPanel();
app.add(panel);
var lb2 = app.createLabel("label1");
lb2.setId("label1");
lb2.setText("簡単なテキスト操作を行います。");
panel.add(lb2);
var tbox = app.createTextBox();
tbox.setName("tbox1");
panel.add(tbox);
var btn = app.createButton("click");
panel.add(btn);
return app;
}
UiAppがややこしく難解な理由の一つが、このイベントハンドラー。HTML ServicesでもJavaScriptによるボタンへのイベントハンドラー設置がありましたが、基本的にはあれと似たようなものです。しかし、問題なのはこのイベントハンドラーが2種類あり、尚且つ資料が少ないために、どのようにして値のやり取りをしたら良いのかが分かりにくい点にあります。
2種類あるというのは、クライアント上で動かすクライアントハンドラー、そして、Googleサーバ上で動かすサーバークライアントの2種類があるという点です。値の取り方は、配置したコントロールにIDやNAMEを設定しておき、それを参照するという形で、JavaScriptのそれと変わりません。
//入力された名前を取得して表示するサンプル
//ベースサンプルは、https://code.google.com/p/google-apps-script-issues/issues/detail?id=2695
function doGet() {
//タイトルの設定とお決まりのコード
var app = UiApp.createApplication().setTitle('テキストボックスから値を受け取って表示するテスト');
var panel = app.createVerticalPanel();
return app.add(panel.add(
app.createLabel('お名前:')).add(
app.createTextBox().setName('doraemon')).add(
app.createButton('送信してみる', app.createServerHandler('doPost').addCallbackElement(panel))));
}
function doPost(e) {
//お決まりのコードと受信した値の表示、およびパネルの削除
var app = UiApp.getActiveApplication();
return app.remove(0).add(app.createLabel('テストありがとう!!'+e.parameter.doraemon));
}
そもそも、2種類あるハンドラーですが、サーバーハンドラーと呼ばれるものが、Google Apps Scriptでは当たり前のものです。なぜなら、このGoogle Apps 自体がサーバサイドで動いているものなのですから。対して、クライアントハンドラーは、あえてそういう仕組なのに、ブラウザ上で処理を行わせるというもので、サーバーにいちいち投げずに、即座に答えをその場で返す(簡単な計算機などのアプリケーションみたいなもの)などで使われるものです。スプレッドシートへの値の読み書きなんかは、クライアントハンドラーでは実行できません(その為、クライアントハンドラーは実行できる要素に制限があります)。
ただし、クライアントハンドラーのほうが速度が遥かに早いので、必要な部分だけサーバーハンドラーを使用し、できうる限り、クライアントハンドラーを使うというのが早く動くスクリプトを作るコツのようです。
クライアントハンドラーを使った事例コード
function doGet() {
var app = UiApp.createApplication()
var chkPaida = app.createCheckBox("みすず飴").setValue(true);
var chkPaidb = app.createCheckBox("おまんじゅう").setVisible(false);
var checkpanel = app.createHorizontalPanel().add(chkPaida).add(chkPaidb)
var dateBox2 = app.createDateBox().setName("paidThrough").setId("paidThru").setName("paidThru");
var Chandlera = app.createClientHandler().forTargets(dateBox2).setEnabled(false).forTargets(chkPaidb).setVisible(true).setValue(false)
.forEventSource().setVisible(false)
chkPaida.addClickHandler(Chandlera)
var Chandlerb = app.createClientHandler().forTargets(dateBox2).setEnabled(true).forTargets(chkPaida).setVisible(true).setValue(true)
.forEventSource().setVisible(false)
chkPaidb.addClickHandler(Chandlerb)
app.add(checkpanel).add(dateBox2)
return app
}
実行結果
※ベースコードは、StackOver Flowから拝借しました。
解説とヒント
このコードは一体何をやっているコードなのかは実行してみるとわかる。箇条書きで列挙すると・・・