スプレッドシートをDB代わりにするWebサイトをGASで試しに作ってみた

##HTMLを返してみる
まずシンプルなHTMLファイルを返してみましょう。

###HTMLファイルを作成

1. 適当なスプレッドシートを作成しApps Scriptエディターを開く
1. 画面左側の下記位置にあるファイルの `+` ボタンからHTMLをクリック
1. ファイル名の入力を求められるので適当な名前をつけましょう  
ここでは `index` としました。ファイル拡張子は自動で `.html` が付きます。

|![スクリーンショット 2021-12-17 15.34.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114743/0617b3e6-17a4-939c-93aa-fc018bac0772.png)|
|:--|

すると以下のコードを含んだ `index.html` が作成されます。便利！:yum:

```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    
  </body>
</html>
```

表示確認のため一旦　`はろーわーるど` の文字列を追加して保存。

```html:index.html
（省略）

  <body>
    はろーわーるど
  </body>
</html>
```

###GASを記述
次に最初からある `コード.gs` に以下のコードを書きます。
この `doGet()` という名前で関数を作成すると、GETアクセスをトリガーに関数を実行することができます。
中身としてはブラウザからのGETアクセスでdoGet()が起動し、ブラウザに対してreturnでindexファイルを返しています。

```js:コード.gs
function doGet() {
  return HtmlService.createTemplateFromFile('index').evaluate();
}
```

書いて保存したら画面右上の `デプロイ` から `新しいデプロイ` をクリック。
このデプロイはこの後もちょこちょこ出てきて、忘れるとコードを書き換えて保存したのに動かない！ということになるので注意してください :pencil:

|![スクリーンショット 2021-12-17 16.31.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114743/85d3651f-a1c8-ba70-e6ac-8e0b1d166c12.png)|
|:--|

`新しいデプロイ` モーダルが開くので、左側の `種類の選択` の歯車から `ウェブアプリ` をクリック。

|![スクリーンショット 2021-12-17 16.32.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114743/ddec7bb6-b415-70c4-f85e-ffc17e369921.png)|
|:--|

`種類の選択` の下に `ウェブアプリ` が表示されるので右側で必要事項を設定して `デプロイ` ボタンをクリック。
`新しい説明文` は未入力でもOKです。
説明を入れておくと `デプロイを管理` での管理が楽です。

![スクリーンショット 2021-12-17 16.46.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114743/a256bdcf-2b21-7056-4d30-6b620a82ac49.png)

するとウェブアプリのURLが発行されるのでコピーします。

![スクリーンショット 2021-12-17 16.50.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114743/886c4438-6f62-ea5e-effa-07636f470c34.png)

コピーしたURLをブラウザに貼り付けてアクセスしてみると `はろーわーるど` と表示されると思います。
これで作ったHTMLファイルに対して、GASのdoGet()経由でブラウザからGETアクセスできるようになりました :tada:

##スプレッドシートの値を返してみる
では次にスプレッドシートの値を取得してブラウザに表示してみましょう。
スプレッドシートの `A1セル` に適当な値を入力しておきます。
ここでは `Hello world` と入力しました。

###GASでスプレッドシートの値を取得
まずGASでスプレッドシートの値を取得します。

```js:コード.gs
function getSsValue() {
  const sh = SpreadsheetApp.getActiveSpreadsheet();
  const value = sh.getRange('A1').getValue();
  return value;
}
```

これでスプレッドシートのA1セルから値を引っ張る関数ができました。
不安な方は　`Logger.log(value)` でちゃんと値を拾えているか確認しておきましょう。

###HTMLからGASを呼ぶ
次に上で作成した `getSsValue()` を `index.html` から呼び出しスプレッドシートの値を表示してみましょう。
最初に表示する領域を用意しておきます。（上で書いた `はろーわーるど` は消しました。）

```html:index.html
（省略）

  <body>
    <div id="top_div"></div>
  </body>
</html>
```

今度はHTMLからGASを呼ぶようにします。
HTMLからGASを実行する場合 `google.script.run.呼び出したいGASの関数` と書けば良いのですが、呼び出すGAS関数に戻り値がある場合は　`.withSuccessHandler（呼び出した関数が成功した時の処理）` と書く必要があります。（必要であれば `.withFailureHandler(呼び出した関数が失敗した時の処理)` も書く。）
こんな感じ。

```js
google.script.run
  .withSuccessHandler(呼び出したGASの関数が成功した時の処理を書く)
  .呼び出したいGASの関数
```

実際に `index.html` に書くコードとしては以下になります。

```html:index.html
（省略）

  <body>
    <div id="top_div"></div>
  </body>
  <script>
    google.script.run
      .withSuccessHandler(function(value) {  // 呼び出したGASの関数が成功した時の処理
        // HTMLのdivを取得
        const div = document.getElementById('top_div');
        // 呼び出した関数の戻り値（value）を表示要素にする
        const textNode = document.createTextNode(value);
        // divにvalueをぶら下げる
        div.appendChild(textNode);
      }).getSsValue();  // 呼び出したいGASの関数
  </script>
</html>
```

`コード.gs` と `index.html` を書いたら再度デプロイを行います。
デプロイ後ブラウザで見てみると、スプレッドシートのA1セルの値が表示されているはずです。

##スプレッドシートの値を変更してみる

スプレッドシートの値の取得はできるようになったので、今度はスプレッドシートの値を書き換えてみましょう。

###GASでスプレッドシートの値を書き換え
では取得と同様に今度はGASでスプレッドシートの値を書き換えます。

```js:コード.gs
function setSsValue(value) {
  const sh = SpreadsheetApp.getActiveSpreadsheet();
  sh.getRange('A1').setValue(value);
}
```

これでGASからスプレッドシートに書き込めるようになりました。
不安な方はテストを。

###HTMLに入力欄を作る
まずGASに値を渡すための入力欄を用意します。

```html:index.html
（省略）

  <body>
    <div id="top_div"></div>
    <input type="text" id="input">
    <input type="button" value="更新" onclick="edit()">
  </body>

（省略）
```

ひとまずこれで入力欄と更新ボタンができました。
試しにデプロイしてブラウザで確認してみましょう。
更新ボタンは見た目だけで中身を作っていないのでクリックしても反応がありません。

###更新ボタンを機能するようにする
というわけで更新ボタンの裏側を作りましょう。
更新ボタンのonclickでedit()という関数を呼ぶようにしたので、edit()関数を作ります。
edit()関数で入力欄の値を取得します。

```html:index.html
（省略）

  <script>
    google.script.run
      .withSuccessHandler(function(value) {  // 呼び出したGASの関数が成功した時の処理

（省略）

    function edit() {
      const value = document.getElementById('input').value;
      alert(value);
    }
  </script>
</html>
```

入力欄の値を正しく取れることを確認するために、更新ボタンをクリックしたら入力欄の内容をアラート表示するようにしました。
デプロイをお忘れなく。

|![スクリーンショット 2021-12-18 22.43.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114743/290d5fd8-8d96-155a-b3ce-5da9021b10c1.png)|
|:--|

出来てますね！:grinning:
では入力欄の値をGASに渡すようにしましょう。
HTMLからGASを実行する場合前回同様 `google.script.run.呼び出したいGASの関数` ですが、今回は呼び出すGAS関数に戻り値がないため `.withSuccessHandler()` は不要です。

```html:index.html
（省略）

  <script>
    google.script.run
      .withSuccessHandler(function(value) {  // 呼び出したGASの関数が成功した時の処理

（省略）

    function edit() {
      const value = document.getElementById('input').value;
      google.script.run.setSsValue(value);
    }
  </script>
</html>
```

これでHTMLの更新ボタンをクリックすることで、GASのsetSsValue(value)関数が呼ばれ、setSsValue関数によって渡されたvalueの値でスプレッドシートのA1セルが更新されます。

###更新ボタンクリック後に画面を更新する
これでブラウザからスプレッドシートの書き換えが出来るようになりました！
が、このままですと更新ボタンを押しても画面に変化がないので、成功したのかどうかも分かりませんね :pensive:
なので更新ボタンを押したら画面をリロードするようにしましょう。

```html:index.html
（省略）

  <script>
    google.script.run
      .withSuccessHandler(function(value) {  // 呼び出したGASの関数が成功した時の処理

（省略）

    function edit() {
      const value = document.getElementById('input').value;
      google.script.run.setSsValue(value);
      document.location.reload();
    }
  </script>
</html>
```

あれ！:cold_sweat: 真っ白いページになった！
これだとダメなようなので、自分のURLを再度叩くようにしてみました。

###更新ボタンクリック後に自身のURLに再アクセスする
まずはGASで自分自身のURLを取得できるようにします。

```js:コード.gs
function getAppUrl() {
  return ScriptApp.getService().getUrl();
}
```

次にHTMLでそのURLにアクセスするようにします。
`<?= GAS関数 ?>` と記述することでGAS関数の実行結果を埋め込めるようです。
Qiita記事の [GAS内のHTML間で遷移したり、パラメーターを渡したりする方法](https://qiita.com/kyamadahoge/items/13105ff8fb00e03faf8a) を参考にしたので詳しくはこちらをご覧ください。

```html:index.html
（省略）

  <script>
    google.script.run
      .withSuccessHandler(function(value) {  // 呼び出したGASの関数が成功した時の処理

（省略）

    function edit() {
      const value = document.getElementById('input').value;
      google.script.run.setSsValue(value);
      window.top.location.href = '<?= getAppUrl() ?>';
    }
  </script>
</html>
```
##まとめ
これで完成です！
コードを全部まとめると

```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <div id="top_div"></div>
    <input type="text" id="input">
    <input type="button" value="更新" onclick="edit()">
  </body>
  <script>
    google.script.run
      .withSuccessHandler(function(value) {
        const div = document.getElementById('top_div');
        const textNode = document.createTextNode(value);
        div.appendChild(textNode);
      }).getSsValue();

    function edit() {
      const value = document.getElementById('input').value;
      google.script.run.setSsValue(value);
      window.top.location.href = '<?= getAppUrl() ?>';
    }
  </script>
</html>
```

```js:コード.gs
function doGet() {
  return HtmlService.createTemplateFromFile('index').evaluate();
}

function getSsValue() {
  const sh = SpreadsheetApp.getActiveSpreadsheet();
  const value = sh.getRange('A1').getValue();
  return value;
}

function setSsValue(value) {
  console.log(value)
  const sh = SpreadsheetApp.getActiveSpreadsheet();
  sh.getRange('A1').setValue(value);
}

function getAppUrl() {
  return ScriptApp.getService().getUrl();
}
```

こんな感じで今回は書きました。
直感的に分かるように今回は関数でまとめたりはしていません。
自分で書く場合はもっと綺麗に書いてみてみてください。
ちなみに今回の内容とは関係ありませんが、Qiita記事の [Google Drive に保存した画像を直接呼び出せるURLの取得](https://qiita.com/codeDiver/items/0394968fa318d9309d33) のやり方でHTMLに画像を貼ることもでき、Googleドライブで完結できるので素敵です。
それではお疲れ様でした :robot::zzz: