クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【GAS】スプレッドシートのデータをGASで読み込んでWebページを作る(CSS、JS設定も)

GAS、はじめました。GAS(Google Apps Script)はスプレッドシート(略してスプシ)などの処理を自動化するイメージだったが、Webアプリも作れるようでやってみました!まずはスプシのデータをWebページに読み込んで、CSS、JSも設定してみました。それではいきましょう!

【目次】

※参考:Web開発環境についてのまとめ
qiita.com

スプシをSPブラウザでも開けまぁす!

スプシはPCではブラウザ、SPではアプリという使い方が基本。しかし、SPブラウザでも見る方法があった!

スプシをアプリで左上の設定アイコンを開く f:id:idr_zz:20201112052016p:plain

詳細を開く f:id:idr_zz:20201112052153p:plain

下の方にスクロールすると… f:id:idr_zz:20201114101238p:plain

ブラウザのURLがある! f:id:idr_zz:20201112052028p:plain

開くとURLをコピーできる(アクセス設定の変更も)。 f:id:idr_zz:20201112052755p:plain

ブラウザで開くとこんな感じ! f:id:idr_zz:20201112052804p:plain

ブラウザ版を触ってみて分かったこと。

  • ブラウザ版は閲覧のみで編集はできない模様(右上の鉛筆アイコンを押すとアプリが開いた)
  • ブラウザでは行や列の固定設定が効かず、表が大きくなったときに見づらい。。
  • 画面の拡大縮小も拡大はできるが縮小がある程度より先はできないっぽい

GASでWebページ化できそう!

SPブラウザで見る時に、固定設定を効かせたい。CSSを自分で設定できればいいんだけどなー。

「スプレッドシート CSS設定」などで調べていると、下記の記事に出会った。

※参考:Google Apps ScriptのWebアプリケーションにCSSでスタイル設定をする

お?GASでCSSを設定?それなら自由自在やん♪


さらにシリーズ記事の源流を辿ると「GASを使ってスプシからWebアプリを作る」という内容だった!

※参考:Google Apps ScriptでWebアプリケーションを作る最初の一歩

※参考:Google Apps Scriptで作るWebページにスクリプトの結果を出力して表示してみる

※参考:Google Apps ScriptでWebアプリケーションにスプレッドシートの値を出力する

※参考:Google Apps ScriptのWebアプリケーションにCSSでスタイル設定をする

はじめの一歩としてWebページを作っているがさらに理解を深めればWebアプリも作れそう。スプシがDBになるということだ。

オラ、ワクワクしてきたぞ。いっちょやってみっかー!

GASでWebページ作ってみた!

早速作ってみた。 f:id:idr_zz:20201113043607j:plain

元のスプシデータがこちら f:id:idr_zz:20201113043648j:plain

Webを開くと最初にJSが動いてアラートが出る f:id:idr_zz:20201113043725j:plain

アラートを閉じるとCSSが効いたWebページが表示される f:id:idr_zz:20201113043738j:plain

スプシ以外のテキストも設定している。

ファイル構成

GASはJSとほぼほぼ同じ記法だったのでかなり分かり易かった。GASで予め用意されているメソッドを利用するJS、というイメージ。

「ツール>スクリプトエディタ」のサイドバーがファイル構成。

f:id:idr_zz:20201113051012j:plain

  • コード.gs:関数設定のファイル
  • appsscript.json:アプリの設定値のjsonファイル
  • index.html:表示されるページのHTMLファイル
  • css.html:CSS設定ファイル
  • js.html:JS設定ファイル

「コード.gs」が初期からあるファイル。拡張子もgsでGASのメソッドを直接書ける。

「appsscript.json」はnpmのpackage.jsonみたいな内容。初期は表示されない。

「index.html」が表示しているページ部分だが、外部のデータを読み込むテンプレートタグを埋め込んでいる。

「css.html」「js.html」はhtmlファイルだが中にはCSS、JS設定を書いている。index.htmlで読み込んでいる。

「コード.gs」のコード

初期ではこのコードが書かれている。

function myFunction() {
  Browser.msgBox("Hello World&quot",Browser.Buttons.OK)
}

これは使わないのでまるまる削除する。

下記の3つの関数を設定している。

function doGet() {
  return HtmlService.createTemplateFromFile('index').evaluate();
}

function getData(alg) {
  if (alg == 'title') {
    return 'ザ・ビートルズ';
  } else if (alg == 'summary') {
    return 'ビートルズのメンバーを紹介します';
  }
}

function include(filename) {
  return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
  • doGet()関数:HTMLページを表示する
  • getData()関数:ページで読み込む文字列データを設定
  • include()関数:ページで読み込む外部ファイルを設定

詳細は後述。

HTMLテンプレート

「index.html」のコード

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <title><?= getData("title"); ?></title>
    <meta name="description" content="<?= getData("summary"); ?>"/>
    <?!= include('css'); ?>
    <?!= include('js'); ?>
  </head>
  <body>
      <h1><?= getData("title"); ?></h1>
      <p><?= getData("summary"); ?></p>
      <table>
      <?
        const myData = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
        for(var i=0;i<myData.length;i++){
          // output.append('<li>' + myData[i][0] + '</li>');
          output._ = '<tr><td>' + myData[i][0] + '</td><td>' + myData[i][1] + '</td></tr>';
        }
      ?>
    </table>
  </body>
</html>

基本的にはHTMLタグだが、一部に <? ?>のテンプレートタグが埋め込まれている。詳細は後述。


このページを表示させる設定は「コード.gs」のdoGet()関数

function doGet() {
  return HtmlService.createTemplateFromFile('index').evaluate();
}
  • 関数doGet()を定義
  • HtmlServiceオブジェクト のcreateTemplateFromFile()メソッドを実行(引数はindex
  • 続いてevaluate()メソッドを実行
  • 結果をreturnで返す

createTemplateFromFile()でファイルを読みこむ。indexでindex.html。evaluate()でHTMLオブジェクトにする。

※参考:Google Apps ScriptでWebアプリケーションを作る最初の一歩

「コード.gs」の設定値を読み込む

「index.html」のheadタグ

    <title><?= getData("title"); ?></title>
    <meta name="description" content="<?= getData("summary"); ?>"/>

「index.html」のbodyタグ

      <h1><?= getData("title"); ?></h1>
      <p><?= getData("summary"); ?></p>

これらに埋め込んでいるテンプレートタグの中でgetData()関数を実行している。引数でデータを指定している。

<?==がつくのは「出力スクリプトレットタグ」と言って関数の結果を表示する機能。

getData()関数は「コード.gs」に設定している

function getData(alg) {
  if (alg == 'title') {
    return 'ザ・ビートルズ';
  } else if (alg == 'summary') {
    return 'ビートルズのメンバーを紹介します';
  }
}
  • 関数getData()を定義(引数はalg
  • もしalgtitleだったら「ザ・ビートルズ」という文字列を返す
  • またはもしalgsummaryだったら「ビートルズのメンバーを紹介します」という文字列を返す

これが先ほどの「index.html」に読み込まれる。

※参考:Google Apps Scriptで作るWebページにスクリプトの結果を出力して表示してみる

スプレッドシートのデータを読み込む

次はスプレッドシートのデータを読み込む。

「index.html」のtabelタグ

      <table>
      <?
        const myData = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
        for(var i=0;i<myData.length;i++){
          // output.append('<li>' + myData[i][0] + '</li>');
          output._ = '<tr><td>' + myData[i][0] + '</td><td>' + myData[i][1] + '</td></tr>';
        }
      ?>
    </table>
  • 変数myDataSpreadsheetAppオブジェクトのgetActiveSheet()メソッド、getDataRange()メソッド、getValues()メソッドを実行
  • for文でmyDataの数分繰り返し
  • output_プロパティをtl、td`タグ(2つ)に
  • 一つ目のtdmyDataの多次元配列i番目の0番目、2つ目のtdi番目の1番目を入れる

こちらの<?=を付けていない。これは「スタンダードスクリプトレットタグ」と言って関数の結果を読み込むのではなくこの中に直接スクリプトを書く時に使う。

※参考:Google Apps ScriptでWebアプリケーションにスプレッドシートの値を出力する


myDataの中のメソッドはGASの組み込みメソッドで、結果は多次元配列になるようだ。

参考記事では1列のデータを読み込んでliタグに入れている

'<li>' + myData[i][0] + '</li>'

この多次元配列[i][0]iが行数、0が列数と見受けられたので01にしたら2列目のデータを読み込めた!そこでtabletdタグにして2列分読み込んだ!

'<tr><td>' + myData[i][0] + '</td><td>' + myData[i][1] + '</td></tr>'

なお、getRange()メソッドを使うとさらに細かいセルの範囲指定ができるっぽい。

※参考:getRangeいろいろ(Google Apps Script、SpreadSheet) - Qiita


参考記事で使われている.append()メソッドはV8エンジンになって少し書き方が変わったようだ。

output.append(sth)

output._ = sth
の形に修正してみたら、V8でも私のところでは動いてくれました。

※参考:JavaScript - GAS エラーの原因がわからない|teratail

※参考:Google Apps Script の暗黙オブジェクト - Qiita

V8ランタイムとは?

※参考:V8 (JavaScriptエンジン) - Wikipedia

つーか、JavaScriptエンジンとは?

※参考:JavaScriptエンジン - Wikipedia


なお、この処理を行う時に、下記の記事と同じエラーがあった。

「表示→マニフェスト ファイル」で「appsscript.json」を表示して、下記の設定を追記する。

"oauthScopes": ["https://www.googleapis.com/auth/spreadsheets.currentonly", "https://www.googleapis.com/auth/spreadsheets"]

「appsscript.json」のコード

{
  "timeZone": "Asia/Tokyo",
  "dependencies": {
  },
  "webapp": {
    "access": "ANYONE_ANONYMOUS",
    "executeAs": "USER_DEPLOYING"
  },
  "exceptionLogging": "STACKDRIVER",
  "oauthScopes": ["https://www.googleapis.com/auth/spreadsheets.currentonly", "https://www.googleapis.com/auth/spreadsheets"], // ここに追記
  "runtimeVersion": "V8"
}

以下、参考記事の手順で設定を適用した。

記事にもあるように「詳細→〇〇(安全ではないページ)に移動で許可」がとてもわかりにくいUIだった(「安全なページに戻る」の方が目立ってつい押したくなるw)

※参考:GASのエラー”呼び出す権限がありません”はマニフェストファイルに問題あり - Qiita

外部ファイル(CSS、JS)を読み込む

HTMLにCSS 、JS設定も行いたい。

「index.html」のheadタグの中で読み込む。

    <?!= include('css'); ?>
    <?!= include('js'); ?>
  • include()関数で外部ファイルを読み込み(引数がファイル名)

<?!=は「強制出力スクリプトレットタグ」と言って、外部ファイルのコードがエスケープ(コード丸ごと表示)されるのを防ぐ。

include()関数は「スクリプト.ga」に設定している

function include(filename) {
  return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
  • include()を定義(引数はfilename
  • HtmlServiceオブジェクト のcreateHtmlOutputFromFile()メソッドを実行(引数はfilename
  • 続いてgetContent()メソッドを実行

いずれもGASの組み込みメソッド

※参考:Google Apps ScriptのWebアプリケーションにCSSでスタイル設定をする

CSS設定

CSS設定はcssファイルではなくhtmlファイルにstyleタグで囲って書く。

「css.html」

<style>
body {
    font-family: sans-serif;
}

h1 {
    font-weight: bold;
    font-size: 1.5em;
}

tr:first-child {
    background: #666;
    color: #fff;
    text-align: center;
}

td {
    border-bottom: 1px solid #ccc;
    padding: 5px;
}
</style>
  • body全体をゴシック体に
  • h1タグを太字で1.5倍サイズに
  • trタグの1行目は背景を濃いグレー、文字を白、センター寄せ
  • tgタグは下線を入れて、余白を5px

これで先ほどのスタイルが当たった!

JS設定

JS設定もjsファイルではなくhtmlファイルにscriptタグで囲って書く。

「js.html」

<script>
alert('ビートルズがやって来る ヤァ!ヤァ!ヤァ!');
</script>
  • ページ読み込み時にアラートで「ビートルズがやって来る ヤァ!ヤァ!ヤァ!」を表示する

CSSもJSもhtmlで作成したが、これってつまりHTMLタグもテンプレート的にモジュール化して埋め込めるってことだよね!ヘッダー、フッターとか。CSS やJSも複雑な構成はモジュール分割できるかも!

ページの表示が不安定。。

作ったページの公開はこちらの手順に従って「全ユーザー」とかにしてみたんだが…

※参考:Google Apps ScriptでWebアプリケーションを作る最初の一歩

PCやSPのいろいろなブラウザでURLを開いてみると、どうもうまく表示されない時がある。。

PCではこのような真っ白な画面になったり… f:id:idr_zz:20201113065001j:plain

調べると、白い画面ではSafariでは「Sandbox」のエラーメッセージがあった。

※参考:Safari:sandboxのエラー - Qiita

Sandboxとは

※参考:https://wa3.i-3-i.info/word16589.html


SPでは「現在アプリを開くことができません」となったり… f:id:idr_zz:20201113065109p:plain

「現在アプリを開くことができません」はブラウザで複数のGoogleアカウントを使っているのが原因だよ、とか。

※参考:現在、ファイルを開くことができません。(GAPGoogle Apps Script) | ハンノマライフ。

でもいまいちアカウント設定を変えても解決したりしなかったりする。


あと、変更がなかなか反映されなかったりもする。

なかなか反映されないのはバージョンの番号をあげるといいらしい。

※参考:Google Apps Script (GAS) で更新が反映されない場合の対処 | ハックノート

また、アプリのURL欄の下の方にある「latest code」という小さいリンク。こちらから開く場合は問題なく最新版が表示される。 f:id:idr_zz:20201113070243j:plain

これは「開発モード」でurlの最後がdevになっている(本番公開用のURLはexec

※参考:GAS webアプリ公開時のexecとdev - Qiita

修正中はこちらで確認する方が良さそう。


ちなみにページ上部に表示されて目立つ「このアプリケーションは、Google ではなく、別のユーザーによって作成されたものです。」 f:id:idr_zz:20201113072941j:plain

これは2017年以降に表示されるようになったらしい。こちらに対処法などがあった。

※参考:Pre-Practice Blog: HTML Serviceで作ったWebアプリの上部に表示されるバナーについて

最後に

ということでスプレッドシートのデータをGASで読み込んでWebページを作りました!CSSやJSを自由に設定できるのはいいですね♪モジュール分割もできるのでスプシをDB代わりにしたCMS見たいのも作れそう。

このページ上にフォームを作って、そこからスプシのデータにCRUD操作してみたい。そうするとWebアプリっぽくなっていくと思う。

それではまた!


※参考:Web開発環境についてのまとめ
qiita.com