3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

複数のhtmlをGASで公開する

Posted at

Google Apps Script (GAS)は、javascriptに非常に近い文法で記述されたスクリプトでGoogleのアプリケーションを動かすことができるサービスであり、htmlで作成したGUIつきのウェブアプリケーションを作ることもできます。

今回は、htmlで作成したUIが複数あり、それをひとつのプロジェクトでうまく公開してやりたい、という場面についてお話ししようかと思います。

開発環境

私はGoogle Claspを用いてTypescript環境でGASを開発していますが、本記事のコードは、函数の引数や戻り値に対する型付けを外せば、おおむね.gsファイルとしても使えるようになっていると思います。無理そうならTypescript PlaygroundなどでJavascriptに変換すれば、ローカルにTypescript環境のない方でもご活用いただけるかと思います。

  • Google Clasp
  • @types/google-apps-script

GASがどのようにhtmlを表示しているか

GASでhtmlファイルを公開するためには、いくつかのステップを踏む必要があります。ここでは本筋ではありませんので、軽くリストアップするにとどめましょう。

  1. htmlファイルと、"doGet"という名前の函数が含まれる.gsファイルを作る(函数の内容は後述します)
  2. スクリプトエディタの「公開」メニューから「ウェブアプリケーションとして公開」を行う

こうすると、ウェブアプリケーションのリンク(ウェブアプリケーションとして公開した際に得られます)を踏んだ際に、Googleのサーバー側にリクエストが送られて、所定のhtmlファイルを読み込み、HTMLOutputが返ってきます。こうしてGASのhtmlを表示することができるのです。

たとえば、ウェブアプリケーションのUIとして、index.htmlの内容を表示したい場合には、次のようにすればよかろうと思います。

doGet.ts
const doGet = (e:GoogleAppsScript.Events.AppsScriptHttpRequestEvent):GoogleAppsScript.HTML.HtmlOutput => HtmlService.createHtmlOutputFromFile("index");

doGet函数の引数eは、公式リファレンスのこのページにあるRequest Parametersというもので、Query Parametersなどが格納されたオブジェクトです。

複数のHTMLファイルを状況に応じて表示する

GASによるアプリケーションの開発においては、「似たようなロジックだけども、複数のUIを提供したい」という場面もあると思います。たとえば、スプレッドシートをデータベースとして使っているプロジェクトにおいて、

  • データを検索し、整形して表示するhtml
  • 新しいデータを追加するためのフォームを表示するhtml

のふたつをアプリケーションとして公開したい、という場面などでしょうか。こうした場合に、それぞれについてGASのプロジェクトを立てたうえで、似たような函数定義をそれぞれでしなければならないのは面倒ですし、メンテナンスの観点からも好ましくありません。

こういった際には、Query Parametersを活用した効率的なUIの公開を行いたいところです。以下、その方法を解説していきます。

Query Parametersにファイル名を渡す

公開したウェブアプリケーションのURLは、https://script.google.com/.../execというような形になっていると思います。この末尾に?page=indexのような形でファイル名を渡しましょう。

Query Parameterを附したURLにアクセスがあるたびに、GAS側のdoGet函数が呼び出されます。このdoGet函数の引数eには、次のような形でQuery Parametersの値が格納されるのでした。

e.parameter = {
	"parameter1": value1,
	"parameter2": value2, // 以下続く
}

したがって、たとえば先のように、表示したいhtmlファイルの名前をpageというQuery Parameterにして渡すとすれば、この値はdoGet函数の中で受け取ることができます。Javascriptで開発されている方は型定義は必要ありませんが、Typescriptの場合はそのまま書くとエラーを吐きますので、まずどのようなparameterを渡すのかを型定義してやりましょう。

doGet.ts
interface WebAppOnOpen extends GoogleAppsScript.Events.AppsScriptHttpRequestEvent {
      parameter: {
            page: string | undefined
      }
}

そのうえで、doGet函数を以下のように定義します。

doGet.ts
const doget =  (e: WebAppOnOpen): GoogleAppsScript.HTML.HtmlOutput => {
      if (e.parameter.page) {
            return HtmlService.createHtmlOutputFromFile(e.parameter.page)
      } else {
            Logger.log("error: Required Query Parameter not Provided!")
            return HtmlService.createHtmlOutput("URLの末尾に「?page=***」の形式で種別を指定する文字列を追加してください。")
      }
}

もちろん、存在しないhtmlファイルが読み込まれた場合に、「そのページは存在しない」ということを表示する処理を書いてもよいと思います。無効なHTMLOutputが返されるとGASデフォルトのエラーページが表示されますので、ここではそれは省略してしまいました。

これで、複数のhtmlファイルをひとつのプロジェクトに入れて、状況に応じて表示し分けられるようになりました。

補足

  • 「ウェブアプリケーションとして公開」で得られるURLは、なんど公開しなおしても(バージョンを上げても)変化しません。したがって、query parameterつきのURLをほかの人に渡してしまっても、アップデートを続けることが可能です。
  • Google Claspで開発している場合、clasp pushを行うとウェブアプリケーションの公開設定がリセットされます。この場合、再度メニューからウェブアプリケーションとして公開しなおす必要があります。私の調べた限り、これは手動で行うしかなさそうです。

蛇足:html側にQuery Parameterの値を渡す

今回は、Query Parametersをページの表示を制御する目的だけに使用していますが、場合によってはフロントのhtml側で利用したい場面もあるでしょう。その場合には、次のようにするとよかろうと思われます。

doGet.ts
interface WebAppOnOpen extends GoogleAppsScript.Events.AppsScriptHttpRequestEvent {
      parameter: {
            [key: string]: string | undefined;
      }
}

const doGet_ = (e: WebAppOnOpen): GoogleAppsScript.HTML.HtmlOutput => {
      if (e.parameter.page) {
            return Object.keys(e.parameter).reduce((htmlTemplate, parameterKey) => {
                  if (e.parameter[parameterKey]) {
                        htmlTemplate[parameterKey] = e.parameter[parameterKey]
                  }
                  return htmlTemplate
            }, HtmlService.createTemplateFromFile(e.parameter.page)).evaluate()
      } else {
            Logger.log("error: Required Query Parameter not Provided!")
            return HtmlService.createHtmlOutput("URLの末尾に「?page=***」の形式で種別を指定する文字列を追加してください。")
      }
}

たとえば、先ほどと同じくスプレッドシートをデータベースにしている場面において、「特定の行のデータだけを取得し、そのデータに基づいてUIを表示したい」などが例として挙げられるでしょうか。

このような方法でhtml側に渡したパラメータ値は、html側では次のようにして取得することができます。

page.html
<script>
const parameter = <?=parameterName?>
</script>

したがって、先ほど挙げたような例は、次のような.gs(ts)と.htmlを用意すれば実現できます。

getData.ts
const getData = (row: number):string => {
      const sheet = SpreadsheetApp.openById(sheetId).getActiveSheet()
      const lastColumn = sheet.getLastColumn()
      return JSON.strinfigy(sheet.getRange(row, 1, 1, lastColumn).getValues())
}
page.html
<script>
google.script.run.withSuccessHandler(responce => {
      console.log(JSON.parse(responce)) // 取得したデータをconsoleに表示
}).getData(Number(<?=row?>))
</script>

この場合、getData函数の返り値として配列を返してしまうと、html側ではnullがconsoleに表示されます。JSON.stringify()をかませ、文字列を返す必要があることに注意してください。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?