このプログラムだと、ReferenceError: 「output」が定義されていません。というエラーが出ています。
「output」というのは、HtmlService.initTemplate() というメソッドにより生成される暗黙オブジェクトです。
Google Apps Script の暗黙オブジェクト
なので、質問文のようにHtmlServiceと関係ない場所(.gsファイルの無関係な関数内)でoutputを使おうとしても「定義されていません」というエラーになってしまいます。
(outputは、テンプレートhtmlファイルのスクリプトレット内で使用することが多いです。createFromHtmlTemplateFile()関数を呼び出す段階でHtmlService.initTemplate()が呼ばれるためoutputが使用可能になります)
そして、テンプレートファイルは、evaluate()の実行時に、スクリプトレット(<? ~~ ?>
等の記号で囲まれた部分)が展開され、完全なHTMLページとなりますが
ページが読み込まれた後にスクリプトレットを再実行することは、基本的にできないことに注意する必要があります。
Because scriptlet code executes before the page is served, it can only run once per page; unlike client-side JavaScript or Apps Script functions that you call through google.script.run, scriptlets can't execute again after the page loads.
(スクリプトレットのコードは、ページが提供される前に実行されるため、1ページにつき1回しか実行できません。クライアントサイドのJavaScriptやApps Scriptの関数をgoogle.script.runで呼び出す場合とは異なり、ページが読み込まれた後にスクリプトレットを再実行することはできません 。)
(ドキュメントより引用)
以上より、ページ更新(F5キーと同じ動作)を行わず、ページを開いたまま チャットデータを更新しようとするような場合、更新データそのものをスクリプトレットを含んだテンプレートにしてしまうと、少々難易度が高くなる気がします。
(方法として、内部フレームにチャットデータを閉じ込めてフレーム部分を部分更新するとかでしょうか・・・)
(※なお、今回の目的のためにcreateFromHtmlTemplateFileが全く使えないというわけではありません。ページの静的な組み立て(初期データの表示)にも使える余地はあります)
ではページ更新を使わず、データを更新するにはどうするかですが、テーブルのDOMを更新するやり方があります。
下記が全体プログラムになります。デプロイ時は、「アクセスできるユーザー」を「全員」に設定してください。
code.gs
js
1 // getActive()を使うと権限エラーとなるため、openById又はopenByUrlを使うこと。
2 var sheet = SpreadsheetApp . openById ( "~~" ) . getSheetByName ( "シート1" ) ;
3
4 function doGet ( ) {
5 var html = HtmlService . createHtmlOutputFromFile ( "index" ) ;
6 return html ;
7 }
8
9
10 function getData ( ) {
11 return sheet . getDataRange ( ) . getValues ( )
12 }
13
14 function send ( text ) {
15 var user = Session . getActiveUser ( ) ;
16 sheet . appendRow ( [ user , text ] ) ;
17 }
index.html
HTML
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <base target="_top">
5 </head>
6 <body>
7 チャット
8 <input type="text" id="text">
9 <button id="button">送信</button>
10 <br>
11
12 <table id="chat-table" border="1" style="table-layout: fixed;">
13 <tr>
14 <th>USER</th>
15 <th>CHAT</th>
16 </tr>
17 </table>
18
19 <script>
20 // 書き込んだデータの最終インデックス
21 var lastrow = 0;
22
23 //最初の読み込み時に実行
24 window.addEventListener('DOMContentLoaded', function(){
25 document.getElementById("button").onclick=sendm;
26 // 一番最初はすぐにデータを読み込む
27 load();
28 setInterval(load, 3000);
29 });
30
31 // チャットデータをコード.gsから取得。
32 function load(){
33 google.script.run
34 .withSuccessHandler(appendTableRows).getData();
35 }
36
37 function appendTableRows(values){
38 if(values == null|| values[0]=='') return;
39
40 var table = document.getElementById("chat-table");
41
42 for(var i = lastrow; i < values.length; i++){
43 var row = table.insertRow(-1);
44 row.insertCell(0).innerHTML = '<td><font bgcolor=blue color="#000000">'+values[i][0]+'</td>';
45 row.insertCell(1).innerHTML = '<td><font bgcolor=blue color="#000000">'+values[i][1]+'</td>';
46 }
47 // 書き込んだデータの最終インデックスを更新する。
48 lastrow = values.length
49 }
50
51 function sendm(){
52 var text = document.getElementById("text").value;
53 document.getElementById("text").value="";
54 google.script.run.send(text);
55 }
56
57 </script>
58 </body>
59 </html>
チャットデータの更新は、下記のようにして行っています。
・load 関数内で、withSuccessHandlerを使って非同期にコード.gsのgetData関数を呼び出し、
getData関数から取得したチャットデータを、appendTableRows に渡す。
・appendTableRows 内で、tableのDOMを更新してテーブルの行を追加する。
js
1 function load ( ) {
2 google . script . run
3 . withSuccessHandler ( appendTableRows ) . getData ( ) ;
4 }
js
1 function appendTableRows ( values ) {
2 if ( values == null || values [ 0 ] == '' ) return ;
3
4 var table = document . getElementById ( "chat-table" ) ;
5
6 for ( var i = lastrow ; i < values . length ; i ++ ) {
7 var row = table . insertRow ( - 1 ) ;
8 row . insertCell ( 0 ) . innerHTML = '<td><font bgcolor=blue color="#000000">' + values [ i ] [ 0 ] + '</td>' ;
9 row . insertCell ( 1 ) . innerHTML = '<td><font bgcolor=blue color="#000000">' + values [ i ] [ 1 ] + '</td>' ;
10 }
11 // 書き込んだデータの最終インデックスを更新する。
12 lastrow = values . length
13 }
なお、同じチャットが重複して表示されないように、lastrow というグローバル変数に、書き込んだデータの最終インデックスを保持しておき、更新のあった分だけ追加 するようにしています。
このスクリプトではまだ「20行だけに固定」というのは示していませんが、基本的に上記スクリプトを拡張することでできます。御自分で考えてみて下さい。
(この場合createFromHtmlTemplateFileを使うことになると思いますが)
上記は素案として作っただけで、まだまだブラッシュアップの余地はたくさんあります。
20行固定表示以外にも、最初のチャットデータ表示をもっと早くする方法を考えたり、チャットデータの転送量を減らす方法を考えたり(上記スクリプトだとチャットデータを全部送っている)、Vueで作り直したりするのも面白いと思います。