G Suite（Google apps script）というサーバレスな環境で、BootstrapとVue.jsを組み合わせたフォーム開発のフォームテンプレ一覧

# はじめに
BootstrapとVue.jsを組み合わせて、サーバレスな環境であるG Suite（Google apps script）でフォームを作成する。

# 本ページでの掲載事項
フォームの基本的な項目のテンプレートを掲載。

# 前提
G SuiteのAPIを使ってHTMLページをフォームとして作成するため、
スプレッドシートからスクリプトエディタを起動させプロジェクトを新規作成しておく。

## コード.gs
主な処理内容
- アプリケーションにアクセスした際にHTMLページを返却
- スプレッドシートに格納された値を取得して辞書型のオブジェクトにして返却

```ruby:コード.gs
function doGet() {
  var html = HtmlService.createTemplateFromFile("index").evaluate().addMetaTag('viewport', 'width=device-width, initial-scale=1, shrink-to-fit=no');
  return html;
}

function getSS(spreadSheetID, sheetName){

  var res = SpreadsheetApp.openById(spreadSheetID)
    .getSheetByName(sheetName).getDataRange().getDisplayValues();

  var keys = res.splice(0, 1)[0];

  return value = res.map(function(row) {
    var obj = {}
    row.map(function(item, index) {
      obj[keys[index]] = item;
    });
    return obj;
  });
}

function getData() {

  var SSID = "--yourSpreadsheetID--";
  var SN = "--yourSheetName--";

  var options2 = getSS(SSID, SN);

  return options2; 
}
```

## index.html
主な処理内容
- レイアウトは、Bootstrapのsampleを利用
- 「入力項目」「disabel状態の入力項目」「Vue.jsの変数の値」を表示の順に項目を設定

```ruby:index.html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <?!= HtmlService.createHtmlOutputFromFile('css').getContent(); ?>
    <?!= HtmlService.createHtmlOutputFromFile('customcss').getContent(); ?>

  </head>
  <body>

  <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
    <a class="navbar-brand" href="#">Navbar</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>

  <div class="collapse navbar-collapse" id="navbarsExampleDefault">
    <ul class="navbar-nav mr-auto">
      <li class="nav-item active">
        <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#">Link</a>
      </li>
      <li class="nav-item">
        <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
      </li>
      <li class="nav-item dropdown">
        <a class="nav-link dropdown-toggle" href="#" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a>
        <div class="dropdown-menu" aria-labelledby="dropdown01">
          <a class="dropdown-item" href="#">Action</a>
          <a class="dropdown-item" href="#">Another action</a>
          <a class="dropdown-item" href="#">Something else here</a>
        </div>
      </li>
    </ul>
    <form class="form-inline my-2 my-lg-0">
      <input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
      <button class="btn btn-secondary my-2 my-sm-0" type="submit">Search</button>
    </form>
  </div>
  </nav>

  <main role="main" class="container">

    <div class="starter-template">
      <h1>Bootstrap starter template</h1>
      <p class="lead">Use this document as a way to quickly start any new project.<br> All you get is this text and a mostly barebones HTML document.</p>
    </div><!-- /.starter-template -->

    <div id="app">
    <div class="alert alert-primary" role="alert">
      input
    </div>
    <div class="form-group">
    <p>
      <label for="input">Example input</label>
      <input class="form-control" type="text" id="input" placeholder="Example input" v-model.trim="input"/>
    </p>
    <p>
      <label for="input">Example input</label>
      <input class="form-control" type="text" id="input" placeholder="Example input" v-model.trim="input" disabled/>
    </p>
    <p>Output: {{  input  }}</p>
    </div>

    <div class="alert alert-primary" role="alert">
      textarea
    </div>
    <div class="form-group">
      <label for="textarea">Example textarea</label>
      <textarea class="form-control" id="textarea" rows="3" v-model.trim="textarea"></textarea>
    </div>
    <div class="form-group">
      <label for="textarea">Example textarea</label>
      <textarea class="form-control" id="textarea" rows="3" v-model.trim="textarea" disabled></textarea>
    </div>
    <p>Output: {{  textarea  }}</p>

    <div class="alert alert-primary" role="alert">
      radio
    </div>
    <div class="form-check"> 
      <input class="form-check-input" type="radio" name="optionsRadios" id="optionsRadios1" value="unnecessary" checked="" v-model="necessary"/>
      <label class="form-check-label" for="optionsRadios1">
      unnecessary
      </label>
    </div>
    <div class="form-check">
    <input class="form-check-input" type="radio" name="optionsRadios" id="optionsRadios2" value="necessary" v-model="necessary"/>
      <label class="form-check-label" for="optionsRadios2">        
      necessary
      </label>
    </div>    
    <div class="form-check"> 
      <input class="form-check-input" type="radio" name="optionsRadios" id="optionsRadios1" value="unnecessary" v-model="necessary" disabled/>
      <label class="form-check-label" for="optionsRadios1">
      unnecessary
      </label>
    </div>
    <div class="form-check">
    <input class="form-check-input" type="radio" name="optionsRadios" id="optionsRadios2" value="necessary" v-model="necessary" disabled/>
      <label class="form-check-label" for="optionsRadios2">        
      necessary
      </label>
    </div>
    <p>Output: {{  necessary  }}</p>
    
    <div class="alert alert-primary" role="alert">
      select from vue
    </div>
    <div class="form-check"> 
      <label for="select">Example select</label>
      <select id="select" v-model="select" class="form-control">
        <option v-for="option in options" v-bind:value="option.value">
          {{ option.text }}
        </option>
      </select>
    </div>
    <div class="form-check"> 
      <label for="select">Example select</label>
      <select id="select" v-model="select" class="form-control" disabled>
        <option v-for="option in options" v-bind:value="option.value">
          {{ option.text }}
        </option>
      </select>
    </div>
    <p>Output: {{  select  }}</p>
    
    <div class="alert alert-primary" role="alert">
      select from spreadsheet
    </div>
    <div class="form-check"> 
      <label for="select">Example select</label>
      <select id="select2" v-model="select2" class="form-control">
        <option v-for="option2 in options2" v-bind:value="option2.value">
          {{ option2.text }}
        </option>
      </select>
    </div>
    <div class="form-check"> 
      <label for="select">Example select</label>
      <select id="select2" v-model="select2" class="form-control" disabled>
        <option v-for="option2 in options2" v-bind:value="option2.value">
          {{ option2.text }}
        </option>
      </select>
    </div>
    <p>Output: {{  select2  }}</p>

    <div class="alert alert-primary" role="alert">
      select from spreadsheet by sort
    </div>
    <div class="form-check"> 
      <label for="select">Example select</label>
      <select id="select3" v-model="select3" class="form-control">
        <option v-for="option3 in options3" v-bind:value="option3.value">
          {{ option3.text }}
        </option>
      </select>
    </div>
    <div class="form-check"> 
      <label for="select">Example select</label>
      <select id="select3" v-model="select3" class="form-control" disabled>
        <option v-for="option3 in options3" v-bind:value="option3.value">
          {{ option3.text }}
        </option>
      </select>
    </div>
    <p>Output: {{  select3  }}</p>

    <div class="alert alert-primary" role="alert">
      checkbox
    </div>
    <div class="form-check">
      <input class="form-check-input" type="checkbox" value="" id="checkbox" v-model="checkbox"/>
      <label class="form-check-label" for="checkbox">
        Check this checkbox
      </label>
    </div>
    <div class="form-check">
      <input class="form-check-input" type="checkbox" value="" id="checkbox" v-model="checkbox" disabled/>
      <label class="form-check-label" for="checkbox">
        Check this checkbox
      </label>
    </div>
    <p>Output: {{  checkbox  }}</p>
    
    </div><!-- /.vue.el.app -->

  </main><!-- /.container -->

  <?!= HtmlService.createHtmlOutputFromFile('js').getContent(); ?>
  </body>
  <?!= HtmlService.createHtmlOutputFromFile('vue').getContent(); ?>
</html>
```

## vue.html
主な処理内容
- vue.methods
    - initOptions3…読み込んだデータのうち条件に一致する行データを取得し、option3に格納

```ruby:vue.js
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>

<script>  
  var app = new Vue({
    el: '#app',
    data: {
      input:'',
      textarea:'',
      necessary:'',
      select:'',
      options: [
        { text: 'One', value: 'A' },
        { text: 'Two', value: 'B' },
        { text: 'Three', value: 'C' }
      ],
      select2:'',
      options2: [],
      select3:'',
      options3: [],
      checkbox:'',
    },
    methods:{
      initOptions2: function(options2){
        this.options2 = options2;
      },
      initOptions3: function(options3){
        this.options3 = options3.filter(function(el,index){
          if (el.Availability == 'available') return true;
        });
      },
    },
    created: function(){
      google.script.run
        .withSuccessHandler(this.initOptions2).getData();
      google.script.run
        .withSuccessHandler(this.initOptions3).getData();
    },
  })
</script>
```

## js.html
主な処理内容
- BootstrapのJSファイルを読み込むためのおまじない。

```ruby:js.html
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
```
## css.html
主な処理内容
- BootstrapのCSSファイルを読み込むためのおまじない。

```ruby:css.html
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
```

## customcss.html
主な処理内容
- Bootstrapのsampleに記載された個別のCSSを抽出。

```ruby:customcss.html
<style>
  body {
    padding-top: 5rem;
  }
  .starter-template {
    padding: 3rem 1.5rem;
    text-align: center;
  }     
  .bd-placeholder-img {
    font-size: 1.125rem;
    text-anchor: middle;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
  }
  @media (min-width: 768px) {
    .bd-placeholder-img-lg {
    font-size: 3.5rem;
     }
  }
</style>
```

# 参考
[Bootstrap > Getting-started](https://getbootstrap.com/docs/4.4/getting-started/introduction/)
[Bootstrap > content > forms](https://getbootstrap.com/docs/4.4/content/reboot/#forms)
[Vue.js > インストール](https://jp.vuejs.org/v2/guide/installation.html)
[Vue.js > フォーム入力バインディング](https://jp.vuejs.org/v2/guide/forms.html)
[Apps Script > リファレンス](https://developers.google.com/apps-script/reference)