おそらくこういうことをなされたいのではないでしょうか。
(後述しますが、下記は非推奨です。推奨する方法については、一番下の**【代替案】を参照してください**。)
Code.gs
js
1function doGet(e) {
2 const t = HtmlService.createTemplateFromFile('index');
3 return t.evaluate()
4}
5
6function tuikatest() {
7 let spread = SpreadsheetApp.openById("1YV************")
8 let meibo = spread.getSheetByName('名簿');
9 let lastRow = meibo.getLastRow();
10 let optionTag = ''
11 // シートから1行ずつ読み取ってoptionタグを構築
12 for (let i = 1; i <= lastRow; i++) {
13 let name = meibo.getRange(i, 1).getValue();
14 optionTag = optionTag + ('<option value="' + name + '">' + name + '</option>');
15 }
16 // 構築したタグを返す。
17 return optionTag
18}
index.html
HTML
1<!DOCTYPE html>
2<html>
3 <head>
4 <base target="_top">
5 </head>
6 <body>
7 生徒名1:<select name="seito1"> <?!= tuikatest() ?> </select>
8 </body>
9</html>
10
質問文のコードから変更したのは下記です。
diff
1 for (let i = 1; i <= lastRow; i++){
2 let name = meibo.getRange(i,1).getValue();
3- return('<option value="'+name+'">'+name+'</option>');
4+ optionTag = optionTag + ('<option value="' + name + '">' + name + '</option>');
5 }
6+ return optionTag;
7}
元の文ではループが開始してすぐreturnしているので、データが複数行あっても1行分しかデータが返ってきません。
上記のように、ループを回してタグを追加し、最終結果を返すようにしています。
index.html
diff
1- <?= tuikatest() ?>
2+ <?!= tuikatest() ?>
通常の出力スクリプトレットだと、タグがエスケープされHTMLとして解釈されません。
したがって代わりに強制出力スクリプトレットを使用しています。
【非推奨の理由】
上記では、optionタグを構築し、強制出力スクリプトレットを使用してHTMLに表示しています。
しかし、強制出力スクリプトレットをユーザーが自由に設定できる値に使用するのは、セキュリティ上危険です。
今回、ドロップダウンに表示するデータをスプレッドシートから読み取っていますが、スプレッドシートには自由にデータを書き込めます。
optionタグを構築して渡すやり方だと、仮に悪意のある人間がスプレッドシートのデータに細工を施した場合、任意のスクリプトを実行できてしまう可能性があります。
(コードインジェクション)
たとえば、「名簿」シートの6行目に下記のような記述をすることで、HTML側にアラートを表示することができます。
強制出力スクリプトレットはタグをエスケープしないため、
html側で
html
1生徒名1:<select name="seito1">
2 <?= tuikatest() ?>
3 </select>
が
html
1生徒名1:<select name="seito1">
2 ...
3 <option value="生徒名6</options><script>alert('test')</script>">生徒名6</options><script>alert('test')</script></option>
4 </select>
と解釈されます。
結果、見た目上は普通のデータがあるだけなのに、裏で<script>タグに記述したスクリプトを実行できてしまうことになります。
(HTML上は、生徒名6の右の引用符が欠けていたり、タグの対応関係がおかしくなっていますが、ブラウザが勝手にエラーを読み飛ばして補完します)
(なお「名簿シートの管理者とcode.gsの作成者は同じであるから実質的な危険はない」等の議論は別の軸の話としてあるかもしれません)
【代替案】
上記ではタグを構築してそのタグの内容をそのまま渡すようになっていたため、scriptタグを記述できてしまう点に問題がありました。
したがって、タグを構築して渡すのではなく、名前リストのデータだけを渡すようにします。
code.gs
js
1function doGet(e) {
2 const t = HtmlService.createTemplateFromFile('index');
3 return t.evaluate()
4}
5
6function getStudentNames() {
7 let spread = SpreadsheetApp.openById("1Y********************")
8 let meibo = spread.getSheetByName('名簿');
9 let lastRow = meibo.getLastRow();
10 // 名前リストを配列として返す
11 let names = meibo.getRange(1, 1, lastRow, 1).getValues();
12 return names;
13}
index.html
html
1<!DOCTYPE html>
2<html>
3 <head>
4 <base target="_top">
5 </head>
6 <body>
7 <select id="students"></select>
8
9 <script>
10 // GAS側のgetStudentNames()関数を呼び出し、その後onSuccess()を呼び出す。
11 google.script.run.withSuccessHandler(onSuccess).getStudentNames();
12
13 // namesには、getStudentNames()の戻り値が入る。
14 function onSuccess(names){
15 let students = document.getElementById("students");
16 names.forEach(e => students.add(new Option(e, JSON.stringify( { value: e } ))));
17 }
18 </script>
19
20 </body>
21</html>
上記コードの場合、データはエスケープして渡されるので、スクリプトが書かれたデータであってもそれが実行されることはありません。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/12/16 01:18
2021/12/16 02:06