--- title: Google Apps ScriptでWebアプリケーションをつくる tags: GoogleAppsScript Web Vue.js author: hyt48 slide: false --- こんにちは、otkと申します。Qiita初投稿です。よろしくお願いいたします。 ## はじめに 最近Google Apps Script(GAS)を触る機会があり、今までほとんど触ったことがなかったのですが、使ってみてかなりいろいろできることに驚きました。その中でGASでWebアプリをつくれるということを知ったので、それについて書いていこうと思います。 ## つくるもの 定番のTodoアプリです。 [こちら](https://cr-vue.mio3io.com/tutorials/todo.html)のチュートリアルのコードを使わせていただきました。(CDN版のVue.jsを使っています。)チュートリアルではデータをローカルストレージに保存していますが、いちばん最後でGoogleスプレッドシートに保存するよう変更していきます。 ## 必要なもの - [Googleアカウント](https://support.google.com/accounts/answer/27441?hl=ja)だけあればOKです。 ## 手順 以下手順で進めます。 1. GASプロジェクトを作成。 2. GASでGETリクエストを受け取り、HTMLを表示するプログラムを書く。 3. Webアプリ公開・動作確認。 4. CSS、JSを別ファイルに分割してTodoアプリ作成。 5. データをGoogleスプレッドシートに保存するように変更。 ### 1. GASプロジェクトを作成。 https://script.google.com/ または Gooleドライブ からGASプロジェクトを作成します。 スクリーンショット 2019-12-18 19.42.43.png ### 2. GASでGETリクエストを受け取り、HTMLを表示するプログラムを書く。 サーバー側GASを記述します。 GASでGETリクエストを受け取るようにするためには[HTML Service](https://developers.google.com/apps-script/guides/html)の`doGet`関数を使います。以下はGETリクエストを受け取ったときに`index.html`ファイルの内容を表示する処理です。 ```js:Code.gs function doGet() { return HtmlService.createHtmlOutputFromFile('index'); } ``` 次にフロント側のHTMLです。 [ファイル] > [New] > [HTMLファイル]で`index.html`ファイルを作成できます。 ```html:index.html Hello! ``` ### 3. Webアプリ公開・動作確認。 これだけで最低限の準備ができたので、公開して動作確認します。 [公開] > [ウェブアプリケーションとして導入]。 スクリーンショット 2019-12-18 20.54.44.png 以下のようにして [Deploy]。 スクリーンショット 2019-12-18 20.55.24.png URLが表示されるので、そちらにアクセスすれば`index.html`の内容が表示されるはずです。これで一応Webアプリを公開することができました。 スクリーンショット 2019-12-18 20.56.04.png スクリーンショット 2019-12-18 20.56.15.png ### 4. CSS、JSを別ファイルに分割する。 Todoアプリを作っていきます。 こちらの[チュートリアルの完成形](https://cr-vue.mio3io.com/tutorials/todo.html#%E5%AE%8C%E6%88%90%E5%BD%A2)の`index.html`, `main.css`, `main.js`を書いていきたいのですが、GASではHTMLファイルとGSファイルのみしか作成できません。ですので` ``` ```html:main.js.html ``` `index.html`で読み込ませるためには以下のように記述します。 ```html:index.html ... ... ... ``` ``の間にGASのコードを記述して処理を実行することができるので、上のように記述することで別ファイルの`main.css.html`, `main.js.html`ファイルを読み込むことができます。 あとは[チュートリアルのコード](https://github.com/mio3io/cr-vue/tree/master/codes/tutorial-todo)と同じです。 以上でTodoアプリを作成・デプロイすることができます。簡単! スクリーンショット 2019-12-19 6.35.55.png ### 5. データをGoogleスプレッドシートに保存するように変更。 最後に、ローカルストレージに保存していたデータを、GoogleスプレッドシートをDBのように使ってそちらに保存したいと思います。 以下のようなスプレッドシートを用意します。 スクリーンショット 2019-12-19 6.41.01.png こちらをGASで取得する処理は以下になります。 ```js:db.gs var DB_SPREADSHEET_URL = 'スプレッドシートのURL'; var ss = SpreadsheetApp.openByUrl(DB_SPREADSHEET_URL); var sheet = ss.getSheetByName('Todo'); // スプレッドシートからデータを読み込む処理。 function getTodosFromDb() { var lastRow = sheet.getLastRow(); var lastCol = sheet.getLastColumn(); var data2dArray = sheet.getRange(1, 1, lastRow, lastCol).getValues(); var keys = data2dArray[0]; var dataObjects = []; for (var row = 1; row < lastRow; row += 1) { var dataRow = data2dArray[row]; var dataObject = toObject(dataRow, keys); dataObjects.push(dataObject); } return dataObjects; } // スプレッドシートにデータを書き込む処理。 function setTodosToDb(todos) { sheet.clear(); var keys = Object.keys(todos[0]); var data2dArray = [keys]; for (var i = 0; i < todos.length; i += 1) { var dataObject = todos[i]; var dataRow = toArray(dataObject, keys); data2dArray.push(dataRow); } var numRows = data2dArray.length; var numCols = keys.length; sheet.getRange(1, 1, numRows, numCols).setValues(data2dArray); } function toObject(array, keys) { var obj = {}; for (var i = 0; i < keys.length; i += 1) { obj[keys[i]] = array[i]; } return obj; } function toArray(obj, keys) { var array = []; for (var i = 0; i < keys.length; i += 1) { array.push(obj[keys[i]]); } return array; } ``` こちらの`getTodosFromDb`メソッドと`setTodosToDb`メソッドをフロント側から呼び出したいのですが、それには`google.script.run`というAPIを使います。 https://developers.google.com/apps-script/guides/html/reference/run?hl=en この`google.script.run`が少し複雑ですので、以下の例で説明します。例では以下のような処理を行なっています。 - サーバー側で定義されたdoSomethingメソッド(①)をフロント側で`google.script.run`で呼び出しています(③)。 - doSomethingを実行し、処理が成功するとonSuccessメソッド(②)が実行されます。(失敗した場合はonFailureメソッドが実行されます。) - このときdoSomethingの戻り値がonSuccessの引数doSomethingReturnValueに渡されます。 ```js:GASサーバー側 // ① function doSomething(arg) { ... return returnValue; } ``` ```html:HTMLフロント側 ``` こんな感じで、このままだと少し使いにくいので、[こちら](https://gist.github.com/torufurukawa/64339baf16efd3598e71dd763d1db0cf#file-google-script-run-promise-js-L11)を参考に、async/awaitが使えるように以下の処理を定義します。 ```js:HTMLフロント側 function scriptRunPromise() { const gs = {}; // google.script.run contains doSomething() methods at runtime. // Object.keys(goog.sscript.run) returns array of method names. const keys = Object.keys(google.script.run); // for each key, i.e. method name... for (let i = 0; i < keys.length; i++) { // assign the function to gs.doSomething() which returns... gs[keys[i]] = (function(key) { // a function which accepts arbitrary args and returns... return function(...args) { // a promise that executes ... return new Promise(function(resolve, reject) { google.script.run .withSuccessHandler(resolve) .withFailureHandler(reject) [key].apply(google.script.run, args); }); }; })(keys[i]); } return gs; } ``` こうすることで以下のような形でサーバー側のdoSomethingメソッドが呼び出せるようになります。 ```js:HTMLフロント側 var gs = scriptRunPromise(); var doSomethingReturnValue = await gs.doSomething('hello'); ``` これを使ってチュートリアルのローカルストレージへのデータ保存部分を以下のように書き換えます。 ```html:main.js.html(変更前) ```  ⬇️ ```html:main.js.html(変更後) ``` 残りは`fetch`, `save`メソッドの呼び出し部分を修正すればOKです。 最終的なコードは[こちら](https://gist.github.com/hyt48/3fae366bd5a884c91965090017fa46bd)です。 ## おわりに 以上がGASでのWebアプリの作成方法になります。 簡単に、なにより無料で使えるのが便利ですよね。 [clasp](https://github.com/google/clasp)というGASをローカルで開発するためのツールがあるので、もっと凝ったことをしたい場合はこれを使うと便利です。 他にも色々便利な使い方があるようなので、色々やってみて、またアウトプットしていきたいと思います。 読んでいただきありがとうございました!