47
45

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 5 years have passed since last update.

GAS+Vue.jsでサーバレスなWebアプリ③

Posted at

#はじめに
GASのHtmlServiceを使ってSPAライクなWEBアプリケーションを書くことになったのでその時のメモです。
こちらの記事の続きとなります。

前回までの記事
GAS+Vue.jsでサーバレスなWebアプリ①
GAS+Vue.jsでサーバレスなWebアプリ②

本記事では、サーバサイド側のGASから取得したスプレッドシートのデータをVueインスタンスを用いて描画してみたいと思います。

#今回のアウトライン

  1. GAS関数呼び出しの基本
  2. クライアントサイド実装
  3. サーバサイド実装
  4. 終わりに

#それではさっそく書いてみるよ。

###1. GAS関数呼び出しの基本
クライアントサイドからのGAS関数の呼び出しは大きく2つの方法があります。
一つは前回の記事でも少しだけ触れたのですが、テンプレートタグを用いる方法です。

<?!= yourServerSideFunction() ?>
こんな感じのタグ内ではサーバサイド側に定義した関数を呼び出せるというやつですね。

詳しい使い方は公式のドキュメントを参照いただけたらと思います。
ぶっちゃけスプレッドシートの内容を描画するだけなら、
こちらの方がシンプルかもしれません。

もう一つがクライアントサイドのJavaScriptでgoogle.script.runを用いる方法です。
今回はGAS+Vue.jsということで、こちらを少し見ていきたいと思います。
基本的にはクライアントサイドに定義したJavaScriptで以下のように呼び出します。

sampleJS.html
<script>
  google.script.run.withSuccessHandler(function(serverResponse){
    console.log("success");
    console.log(serverResponse);
  }).withFailureHandler(function(serverResponse){
    console.log("failure");
    console.log(serverResponse);
  }).yourServerSideFunction();
</script>
sample.gs
function yourServerSideFunction(){
  return 'response from server side';
}

クライアントサイドからgoogle.script.runを用いて
サーバサイドの関数を呼び出す処理は非同期に実行されます。
従ってyourServerSideFunction()の返り値を利用して何かしらの処理を行うには
返り値をコールバック関数の引数として渡してあげる必要があります。

通信成功時にはwithSuccessHandler(callbackFunc),
失敗時にはwithFailureHandler(callbackFunc)が実行されます。
蛇足かもしれませんが実際にyourServerSideFunction()の返り値が渡されるのは
callbackFunc(arg)ですのでご注意を。

jQueryやSuperAgentを使ってAjax処理を書いたことのある方は馴染みある書き方かもしれませんね。
ちなみにwithSuccessHandler(callbackFunc)withFailureHandler(callbackFunc)は省略可能です。
より正確な情報は公式のこちらに書いてあります。

###2. クライアントサイド実装
それでは早速実装に移りましょう。

index.html
<!DOCTYPE html>
<html>
<head>
  <base target="_top">
  <meta charset="utf-8">
  <?!= include('css'); ?>
  <!-- vue.js -->
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
  <div id="SS-table">
    <table>
      <tr>
        <th v-for="item in header">{{ item }}</th>
      </tr>
      <tr v-for="row in rows">
        <td v-for="item in row">{{ item }}</td>
      </tr>
    </table>
  </div>
  
  <?!= include('js'); ?>
</body>
</html>
css.html
<style>
  table {
    border-collapse: collapse;
  }
  
  table th, table td {
    border: solid 2px gray;
  }

  table th{
    text-align: left;
    padding: 10px;
    color: #486666;
    background: #b5ffff;
  }

  table td{
    padding: 4px 10px;
    color: #486666;
  }
</style>
js.html
<script> 
  var vm = new Vue({
    el: "#SS-table",
    data: {
      tableArray: [[]]
    },
    computed: {
      header: function(){
        return this.tableArray[0];
      },
      rows: function(){
        return this.tableArray.slice(1);
      }
    },
    methods: {
      initData: function(ssData){
        this.tableArray = ssData;
      }
    },
    created: function(){
      google.script.run
        .withSuccessHandler(this.initData)
        .withFailureHandler(function(arg){
          console.log(arg)
          alert("データの取得に失敗しました。");
        }).getSheetData();
    }
  });
</script>

index.htmlcss.htmlに関しては特に問題無いと思いますので、js.htmlを見ていきましょう。
先述の通りgoogle.script.runは非同期で実行されるため、
返り値をdataプロパティに初期値として設定することはできません。
そこで今回はcreatedプロパティを用いて
サーバサイドからの返り値をdataプロパティにセットしています。

getSheetData()の返り値が
withSuccessHandler(callbackFunc)
コールバック関数であるinitData(ssData)の引数として渡され、
初期化が実行されるという流れですね。

これでサーバサイドからの返り値を使ってVueインスタンスを作成する仕組みが整いました。

###3. サーバサイド実装
サーバサイドの実装に移りましょう。
クライアントサイドではスプレッドシートの値を返すであろう関数getSheetData()が呼び出されていました。
こちらの関数をserver.gsに追加します。

server.gs
function doGet(request) {
  var template = 'index';
  return HtmlService.createTemplateFromFile(template).evaluate();
}

function include(filename) {
  return HtmlService.createHtmlOutputFromFile(filename)
      .getContent();
}

function getSheetData(){
  var spreadSheetID = "yourSpreadsheetID";
  var sheetName = "シート1";
  var res = SpreadsheetApp.openById(spreadSheetID)
    .getSheetByName(sheetName).getDataRange().getValues();
  
  return res;
}

うん。GASを使ってスプレッドシートをゴニョゴニョしたことがある方ならば見慣れた処理だと思います。
よく分からないという方もスプレッドシートの内容を二次元配列にして返すという理解でOKです。
今回はこんな感じのスプレッドシートをサンプルに用意してみました。
スクリーンショット 2018-04-19 22.11.35.png

これで準備が整いました!!
今回新たにスプレッドシートへのスコープが必要になるので
アプリケーションを更新してスコープを追加しておきましょう。
一番はじめの記事でアプリケーションの公開を行ったときと同様の手順で更新ができます。

いざアクセスしてみると……
スクリーンショット 2018-04-19 22.26.02.png

なんてこったす。
ブラウザのコンソールでログを確認すると以下のようなメッセージが表示されています。
Error: スクリプトが完了しましたが、返された値はサポートされている戻り値の型ではありませんでした。
公式のドキュメントを見てみると以下のような記述が見つかります。

Most types are legal, but not Date, Function, or DOM element besides form; see description

なるほど、どうやらスプレッドシートA列の日時列をgetValues()した際に
DateオブジェクトとなりGASの制約に引っかかったみたいです。

サーバサイドのgetSheetData()関数を修正し
Dateオブジェクトが返り値に含まないようにしましょう。

server.gs
function getSheetData(){
  var spreadSheetID = "yourSpreadsheetID";
  var sheetName = "シート1";
  var res = SpreadsheetApp.openById(spreadSheetID)
      .getSheetByName(sheetName).getDataRange().getValues();
  var formateDate = function(d){
    var addZero = function(n){
      return (n < 10)? "0" + String(n):String(n);
    }
    
    var year = String(d.getFullYear());
    var month = addZero(d.getMonth() + 1);
    var date = addZero(d.getDate());
    var hour = addZero(d.getHours());
    var minute = addZero(d.getMinutes());
    var second = addZero(d.getSeconds());
    return year + "/" + month + "/" + date + " " + hour + ":" + minute + ":" + second;
  };
  
  /* 有効な返り値に変換 */
  for(var i = 0; i < res.length; i ++){
    for(var j = 0; j < res[0].length; j ++){
      if(Object.prototype.toString.call(res[i][j]) == '[object Date]'){
        res[i][j] = formateDate(res[i][j]);
      }
    }
  }
  
  return res;
}

ちなみに別にここまでしなくとも…という方は、
getValues()の代わりにgetDisplayValues()を使うと
強制的に全てのセルをStringとして取得できるので一撃で事が足ります。

さて、それでは満を持して再びエンドポイントへアクセスすると…
スクリーンショット 2018-04-19 22.45.44.png

お疲れ様でした!!!

###4. 終わりに
今回はスプレッドシートの内容をVueインスタンスを用いて描画してみました。
少し複雑なことをしようとすると、GAS特有の制約に意図せずハマったりするので気をつけたいところです…
それでもGASは公式のドキュメントが豊富でしっかりしている上に手軽に使えるので便利だなあと思います。

ここまでの3回の記事でGAS上での実装に必要な大体揃ってきたと思います。
次回はGoogle Driveへのファイルアップローダの実装について書いていきます。

47
45
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
47
45

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?