スプレッドシートをDB代わりにGASのWebアプリを作成しデータ更新させてみた。Vue版 その１～～

#はじめに
「SPA」という言葉を耳にし、「SPAとは、Single Page Application（シングルページアプリケーション）のことで、単一のWebページでアプリケーションを構成する」とは、なんてスゲーんだと思いました。
では、こしらえた「[スプレッドシートをDB代わりにGASのWebアプリを作成しデータ更新させてみた２](https://qiita.com/yamamow/items/1c039176e5636803224d)」をこのSPAにしてみようではないかというのがことの始まりです。

データは前回と同じように吉野家さんの店舗データなのですが、内容は、適当に入れたデータなので、そこんところよろしくお願いします。吉野家がうまいのは事実です。
データを作成するに当たり吉野家さんのHPを見ていましたら、ふと気が付きました。
「うまい、やすい、はやい」だけでなく、「ずっと。」が付いてました。いいですねぇ。

#なにでSPAにするか
Vue、Angular、Reactなどがあるようですが、今回は、
1. 学習コストが低い
2. 小規模である
ということから「Vue.js」で行うことに決めました。

※Vueうんぬんについては先人がいろいろ教えてくれてますので、そちらを参照してください。

#ここでやっていないこと
コンポーネント化ということは今回行っておりません。
コンポーネント化するとソースが見やすくはなりそうだったのですが、親と子の間でのデータ受け渡しが面倒に感じられたので、今回は行いませんでした。今後、余力があればやってみたいと思います。

#それなりに変更が必要（当たり前か）
前回から変更が必要になっていますので、それらを列挙します。

###1. データの持ち方
データシートの２行目を追加しました。
これはhtml内のidやname情報に使用します。

###2. doGet、doPost
doGetは必要です。初めにhtmlファイルを呼び出すために必要なので、その記述だけになります。
doPostは変更内容や次に表示するhtmlファイルの交通整理を行っていましたが、Postはされないですし、１ページで完結するので、doPostそのものが要らなくなります。

###3. データ更新画面と新規登録画面のファイルが不要
前途の通りで更新画面と新規登録画面には遷移しませんので不要となります。
が、更新と新規はモーダル画面を表示し更新、登録するような画面設計にしました。
モーダルにするためのファイルは今回は用意しません。
vue_index.html内にモーダルの内容を記載しています。

###4. 新規に作成したファイル1
vue_js.html。 普通ならVue.jsなんでしょうがGASなのでvue_js.htmlという名前にしておきました。
表示用のhtmlファイルからインクルードして使用します。
このファイルはVueで使用するデータをプロパティとして入れ物を用意しておくファイルになります。
また、表示用HTMLファイルからのonClickで呼ばれたメソッドを記述しておき、コード.gsのメソッドを呼び出す橋渡しをするファイルです。（戻り値があればそれを受けます）

###5. 新規に作成したファイル2
vue_index.html。index.htmlでよかったのですが、Vueを使わないで作っていたものがあったのでvueで作っているファイルだよ、ということでvue_index.htmlとしました。
リスト表示、データ更新、新規登録の機能が入っていますが、データ更新と新規登録がだいたい同じになったので冗長が少しはなくなったのではないかと思います。

#ファイル構成
ということでファイル構成はこのようになります
1. コード.gs
2. vue_index.html
3. vue_js.html
4. css.html

#ハマったところ
ハマったところがあり、それを含めると長くなるので別で投稿しました。
もし、同じようなお悩みがある方の参考になれば幸いです。

[スプレッドシートをDB代わりにGASのWebアプリを作成しデータ更新させてみた。Vue版、ハマったところinputの再描画編~~](https://qiita.com/yamamow/items/ad967a1dbfda6f9e4aa8)

[スプレッドシートをDB代わりにGASのWebアプリを作成しデータ更新させてみた。Vue版、ハマったところcheckboxの再描画編~~](https://qiita.com/yamamow/items/d5c353e6d52861007278)

#コード説明
###コード.gs
基本的にコード.gsのやることは変わりません。前途の通り、doGet、doPostが変更になります。
後は、データの持ち方の都合上要らなくなったfunctionや追加したfunctionがあります。
まぁ、通常の書き方ということで。

###初期表示
ファイルを１つずつ説明しようと思ったのですが、そうもいかないようなので、vue_indexファイルを基準に、その場所の関連する内容で説明しようと思います。

まずはheadから、ハマったナゴリでキャッシュを残さない設定が入っておりますw
cssファイルのcss.htmlをインクルードしています。（vue_jsファイルはソースの下でインクルードしてます）
そして、vueのリンクですね。（まぁ、ここはOKですね）

```html:vue_index.html
  <head>
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Cache-Control" content="no-cache">
    <meta http-equiv="Expires" content="0">
    <base target="_top">
    <?!= include('css'); ?>
    <script src="https://unpkg.com/vue"></script>
  </head>
```

bodyタグの次のdivタグのidでvueのvmを指定します。

```html:vue_index.html
<body>
Vueで作成したページです
  <div id="vue_example">
```

上記でidに設定したvue_exampleをした↓のnew Vueのインスタンス名として「el:」にも設定しています。２つ同じ名称にしているところが多かったのでそのままにしていますが、２行目の「el:」がHTML内のタグのidと同じならいいんじゃないかと思います。（違ったらごめんなさい）
そして、dataで各プロパティを宣言します。
スプレッドシートからデータを取得すると、２次元配列になるので最初から２次元配列にしています。
初期表示の状態が分からなかったので[['初', '期', '表', '示', '中', 'です']]という配列を初期で設定しています。（なので、実行すると「初期表示中」と表示される）
何のプロパティなのかは以下のソースを参照願います。

```html:vue_js.html
<script>
var vue_example = new Vue({       //ここと
    el: '#vue_example',           //ここ

    data:{
        listtitles:[['初', '期', '表', '示', '中', 'です']],  // リストの項目名
        listitems: [['初', '期', '表', '示', '中', 'です']],  // リストのデータ
        dispDetails: false,                                  // モーダル表示判定
        showContent: false,                                  // モーダル表示内の詳細データ表示判定
        searchword: '',                                      // 検索文字列
        refitem:[],                                          // 詳細データ
        initOptions:[['初期値']],                             // 詳細画面のマスターデータ
    },
```

続いてボタンですが、その前にinputのテキストの説明を。
「v-」とついているところがvueの設定箇所になります。
まず、「v-model="searchword"」は、モデルとしてsearchwordプロパティを使うということを表していて、このテキストボックスに文言を入れると、プロパティにその文言が入ってしまうんですね。スゴイ！

```html:vue_index.html
    <input type=text name=searchword v-model="searchword" />
```

で、続いてボタンです。
「v-on:click」はクリックしたときにvue_js.html内に書かれたメソッドを実行しろ。ということになります。まぁ、これはそのまま受け入れられますよね。「検索クリア」時のメソッドの中に、searchwordプロパティを空にしろという命令を書いておきます。そうすると、searchwordプロパティが空になるのは当たり前ですが、テキストボックスも空になります。スゴイ！！
次の新規登録ボタンは、クリックするとモーダルが開くので説明は後程。

```html:vue_index.html
    <button v-on:click="search_word()" class='btn-radius-blue'>　検索　</button>&nbsp;
    <button v-on:click="search_word_clea()" class='btn-radius-blue'>　検索クリア　</button>&nbsp;
    <button v-on:click="add_disp()" class='btn-radius-blue'>　新規登録　</button><br><br>
```

次はデータのリスト表示ですが、まずテーブルの項目名から。
項目名はデータシートの１行目に入っています。
listtitlesにデータを入れています。
listtitlesは、２次元配列なので、以下のように２回まわしてデータを取得、表示しています。
trタグでv-forで回し、titlesに入れ
次のthタグ内でtitlesを更に回して、title で表示しています。
{{ title }}と２重カッコで囲むことにより文言を表示できます。簡単スゴイ！！！
下記のようにインデックス[idx]も使うことが可能です。（使ってないですが）

```html:vue_index.html
    <table>
      <tr v-for="(titles,idx) in listtitles">
        <th v-for="title in titles">{{ title }}</th>
        <th>ボタン</th>
      </tr>
```

表示されるとこんな感じです。↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207568/20c7d0ce-8444-b2e3-61e1-c25bae0f74c1.png)

続いてデータ部です。
データ部は、listitemsプロパティにデータが入っており、trタグで１回まわしています。
更に、tdタグでまわし、{{ tmp }}としてデータを表示しています。
そして最後に詳細画面を表示するためのボタンを設定しています。
buttonタグで「v-on:click」で「ref_item( idx )」を指定しています。
「idx」はループした時のインデックスになります。
クリックされたら、そのインデックスをパラメータに「ref_item」メソッドを実行することになります。

```html:vue_index.html
      <tr v-for="(it,idx) in listitems">
        <td v-for="tmp in it">{{ tmp }}</td>
        <td><button v-on:click="ref_item( idx )" class='btn-radius-blue'>詳細表示</button></td>
      </tr>
```
ひとまず、リスト表示はこんな感じ。（更新日付の新しい方から１０件表示にしています）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207568/803218fb-f6d9-2682-4891-61e7586a5143.png)

ここまでは初期表示なのですが、初期表示はvue_js.html内のcreateが呼ばれます。
この中で、データの取得と、項目名６個の取得を行っています。
以下の「//リスト取得」を例に説明すると
「google.script.run.withSuccessHandler」のような形で「コード.gs」内の「getSheetData」functionを呼びます。
読み込みに失敗すると「withFailureHandler」に入り、アラートで「データの初期取得に失敗しました。」と表示されます。
成功すると戻り値が「this.initData」メソッドのパラメータとして入ります。

```javascript:vue_js.html
    // 初期表示
    created: function() {
               //リスト取得
               google.script.run.withSuccessHandler(this.initData)
                                .withFailureHandler(function(arg){
                                   alert("データの初期取得に失敗しました。");
                                 }).getSheetData();

                //リストの項目名称6個を取得    
                google.script.run.withSuccessHandler(this.initTitles)
                                 .withFailureHandler(function(arg){
                                    alert("リスト項目名の初期取得に失敗しました。");
                                 }).getItemNameList(6);
    } 
```

下記の「methods」内の「initData」functionが呼び出され、そのパラメータである「ary」にデータが入ります。そして「this.listitems = ary;」とあるように、listitems プロパティにデータがセットされます。後は、先に書きました「vue_index.html」の処理になります。

```javascript:vue_js.html
    methods:{
        // データプロパティ
        initData: function(ary){       // このaryに入る
                        this.listitems = ary; 
                  },
```
ここまでで初期表示が終わりました。
長いので、いったんここで切ります。

次回は[スプレッドシートをDB代わりにGASのWebアプリを作成しデータ更新させてみた。Vue版 その２～～](https://qiita.com/yamamow/items/38ad08c3cf85ec9df0e7)になります。