とりあえず、公式リファレンスであるこのあたりを読んでみた。
Communicate with Server Functions
以下、要点まとめ。
Create and Serve HTML
スクリプトの作成からWebアプリとして公開するまで
新規 > Google Apps スクリプト
新規作成 > HTMLファイル
で index.html
を作成。
コード.gs
と index.html
にそれぞれ以下を記述。
コード.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('index') .setSandboxMode(HtmlService.SandboxMode.IFRAME); }
index.html
<html> <body> Hello, world! </body> </html>
保存したら、
公開 > ウェブアプリケーションとして導入...
を開く。
「プロジェクト バージョン」では「新しいバージョンを保存」をクリック。
「次のユーザーとしてアプリケーションを実行」はどちらでも。
最後に「導入」をクリックすると無事アプリケーションとして公開され、URL が取得できるのでアクセスすると Hello, World! と表示されるはず。
Communicate with Server Functions
<script> google.script.run.[関数名]() </script>
で .gs
側の関数を呼ぶことができる
- 一度に10個まで同時に実行可能(その前にブラウザの制限に引っかかるはず)
- 非同期なので順番は保証されない
Paremeters and return values
- クライアント側からサーバ側の関数を引数つきで呼ぶこともできるし、
サーバ側の関数の戻り値は success handler(後述) に引数として渡される 引数・戻り値に渡せるもの
Number
,Boolean
,String
null
や配列、プリミティブ型からなるオブジェクト- (引数のみ) form element
渡せないもの
Date
,Function
- form 以外の DOM
サーバー側に渡した object は参照ではなくコピー
Success handlers & Failure handlers
基本形としてこのようにメソッドチェーンで書く。
index.html
<script> function [サーバ側の関数が正常終了した時に呼ぶ関数]([サーバ側の関数の戻り値]) { // do something } function [サーバ側の関数が失敗した時に呼ぶ関数]([エラーオブジェクト]) { // do something } google.script.run .withSuccessHandler([サーバ側の関数が正常終了した時に呼ぶ関数]) .withFailureHandler([サーバ側の関数が失敗した時に呼ぶ関数]) .[サーバ側の関数]([引数]); </script>
User objects
クライアント側の handler に引数として渡せるのは通常サーバ側の戻り値1つだが、
withUserObject
を使うと第二引数以下に任意の object を渡すことができる。
これによって同じ handler を使い回すことが可能。
<script> function updateButton(email, button) { button.value = 'Clicked by ' + email; } </script> <input type="button" value="Not Clicked" onclick="google.script.run .withSuccessHandler(updateButton) .withUserObject(this) .getEmail()" />
function getEmail() { return Session.getActiveUser().getEmail(); }
この例における this
は button 自身を指す
- 引数を複数渡したい時は?
無理矢理やるならこう。
<script> function updateButton(email, params) { var button = params[0], message = params[1]; button.value = 'Clicked by ' + email + ': "' + message + '"'; } </script> <input type="button" value="Not Clicked" onclick="google.script.run .withSuccessHandler(updateButton) .withUserObject([this, 'hello world']) .getEmail()" />
Private functions
- サーバ側の関数名の末尾に
_
(アンダースコア) をつけたものは private function となる google.script
で呼べない- ライブラリ化しても同様
Templated HTML
- html 側で解釈・実行されるスクリプトのこと
- 3種類ある(後述)
- Scriptlets はページがクライアントに提供される 前 に実行されるため、1ページにつき一度しか実行されない
- テンプレートを使う時はサーバ側は
HtmlService.createTemplateFromFile('index').evaluate()
という書き方になる- 単純な HTML の場合は
HtmlService.createHtmlOutputFromFile('index')
だった createTemplateFromFile(fileName)
が返すのはHtmlTemplate
クラスのオブジェクト- これを
evaluate()
した結果はHtmlOutput
クラスのオブジェクト =createHtmlOutputFromFile(fileName)
と同じもの
- 単純な HTML の場合は
3種類のScriptlets
1. Standard scriptlets
<? ... ?>
で記述されるもの- タグ内の文字はスクリプトとして実行されるが、ページに 表示はされない
2. Printing scriptlets
<?= ... ?>
で記述されるもの- コードの実行結果を contextual escaping を使ってページ内に出力する
- contextual escaping
- 出力先が html なのか、
<script>
タグの中なのか、など自動的にコンテキストを解釈してエスケープしてくれる(っぽい)
- 出力先が html なのか、
- NOTE:
<?= 'Hello, world!; 'abc' ?>
=> Hello, world! のみ出力(first argument のみ)
3. Force-printing scriptlets
<?!= ... ?>
で記述されるもの- 基本は printing scriptlets と同じだが、contextual escaping を行わない
- セキュリティの観点からも、基本は printing scriptlets を利用した方がいい
- ユーザーの入力した文字列を出力するのに force-printing は危険
Apps Script code in scriptlets
scriptlets で Apps Script を実行することができる。
以下、その3つのパターン(3だけコードサンプルあり)
注) いずれの方法もページをロードした時に1度だけしか実行できない、というのは上述した通り
- scriptlets 内でApps Scriptの関数を呼び出す
- scriptlets 内で直接 Apps Script の API を使う (
SpreadsheetApp
など) - Pushing variables to templates
HtmlTemplate
オブジェクト XXX
に対して XXX.[変数名]
で変数に値を渡すことができる。
function doGet() { var t = HtmlService.createTemplateFromFile('index'); t.data = SpreadsheetApp .openById('1234567890abcdefghijklmnopqrstuvwxyz') .getActiveSheet() .getDataRange() .getValues(); return t.evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME); }
<table> <? for (var i = 0; i < data.length; i++) { ?> <tr> <? for (var j = 0; j < data[i].length; j++) { ?> <td><?= data[i][j] ?></td> <? } ?> </tr> <? } ?> </table>
TODO
公式リファレンスとしては残り2ページ。
あとは、、、
- 外部のJavascriptライブラリがどこまで使えるか
- だいたい使える、とのことだけど jQuery 以外は期待しない方が...?
- HtmlService で Polymer 使っている例があってこれは面白そう。
(だが、やり方がやや強引ぽい)- http://googleappsdeveloper.blogspot.jp/2014/12/speeding-up-htmlservice.html
- Polymer がまだCDNにないからGAEに静的リソースとして上げてそれを読み込む、とかやってるらしい