VueFormGenerator で柔軟にFormを自動生成する

[VueFormGenerator](https://github.com/vue-generators/vue-form-generator) は、schemaからFormを自動生成するライブラリです。

本家のサンプルでは、schemaをフロントエンド側で定義していますが、サーバーからschema情報を送ることでフロントエンドを変更することなく、いろんなフォームを作成できます。


## サンプル

vue側の実装は以下のような感じです。 


```vuejs
<template>
  <div class="container">
    <div class="panel panel-default">
      <div class="panel-heading">Dynamic Form: {{type}}</div>
      <div class='panel-body'>
        <vue-form-generator :schema='schema' :model='model' :options='formOptions'></vue-form-generator>
      </div>
      <button class="btn btn-primary" v-on:click="send">Submit</button>
    </div>
  </div>
</template>

<script>
import axios from 'axios'
import VueFormGenerator from 'vue-form-generator'
import 'vue-form-generator/dist/vfg.css'
import Vue from 'vue'
Vue.use(VueFormGenerator)
VueFormGenerator.validators.resources.fieldIsRequired = '必須項目だよ'
VueFormGenerator.validators.resources.textTooSmall = '短すぎ!! : 今{0}文字, 最低{1}文字入力してね'

export default {
  mounted: function () {
    var that = this
    const url = '/api/' + this.type
    axios.get(url).then(function (response) {
      that.model = response.data.model
      that.schema = response.data.schema
    })
  },
  data () {
    return {
      model: null,
      schema: null,
      formOptions: {
        validateAfterLoad: true,
        validateAfterChanged: true
      },
      type: this.$route.query.type
    }
  },
  methods: {
    send: function (evnet) {
      alert('POST /' + this.type + '  body:' + JSON.stringify(this.model))
    }
  }
}
</script>
```

全体はgithub においてます。
https://github.com/ihgs/sample-vue-form-geneartor


### 詳細


* vue-form-generator コンポーネントを定義し、 `<script>` 内で、 `schema`, `model`, `formOptions` を定義します。


```vuejs
<vue-form-generator :schema='schema' :model='model' :options='formOptions'></vue-form-generator>
```

* `model`, `schema`は、サーバーから取得するので`null`にしておきます。  
 `type`：サーバーの取得データを切り替えるために、今回はパスのquery を利用します。

```javascript
data () {
    return {
      model: null,
      schema: null,
      formOptions: {
        validateAfterLoad: true,
        validateAfterChanged: true
      },
      type: this.$route.query.type
    }
  }
```

* `/api/{type}` から取得した値を、`model`, `schema` にセットします。

```javascript
mounted: function () {
    var that = this
    const url = '/api/' + this.type
    axios.get(url).then(function (response) {
      that.model = response.data.model
      that.schema = response.data.schema
    })
  }
```

* validation のエラー文は、以下のようにして上書き可能です。  
  resource 名は、[ソース](https://github.com/vue-generators/vue-form-generator/blob/master/src/utils/validators.js)で確認しましょう。

```javascript
VueFormGenerator.validators.resources.fieldIsRequired = '必須項目だよ'
VueFormGenerator.validators.resources.textTooSmall = '短すぎ!! : 今{0}文字, 最低{1}文字入力してね'
```


### 動作

サーバーから返す値を以下のようにすると

```json
{
  "model":{
    "id": 1,
    "name": "John Doe",
    "password": "J0hnD03!x4"
  },
  "schema": {
    "fields":
    [
      {
        "type": "input",
        "inputType": "text",
        "label": "ID (disabled text field)",
        "model": "id",
        "readonly": true,
        "disabled": true
      },
      {
        "type": "input",
        "inputType": "text",
        "label": "Name",
        "model": "name",
        "placeholder": "Your name",
        "featured": true,
        "required": true,
        "validator": "string"
      },
      {
        "type": "input",
        "inputType": "password",
        "label": "Password",
        "model": "password",
        "min": 6,
        "required": true,
        "hint": "Minimum 6 characters",
        "validator": "string"
      }
    ]
  }
}
```

![vue_gen1.png](https://qiita-image-store.s3.amazonaws.com/0/51316/a21c6a5b-2c3c-3719-8758-cb88d3e5533c.png)

となり、以下のようにすると

```json
{
  "model":{
    "skills": "Javascript",
    "email": "john.doe@gmail.com",
    "status": true
  },
  "schema": {
    "fields":
    [
      {
        "type": "select",
        "label": "skills",
        "model": "skills",
        "values": [
            "Javascript",
            "VueJS",
            "CSS3",
            "HTML5"
        ]
      },
      {
        "type": "input",
        "inputType": "email",
        "label": "E-mail",
        "model": "email",
        "placeholder": "User's e-mail address",
        "validator": "email"
      },
      {
        "type": "checkbox",
        "label": "Status",
        "model": "status",
        "default": true
      }
    ]
  }
}
```

![vue_gen2.png](https://qiita-image-store.s3.amazonaws.com/0/51316/826f266d-857a-9645-d088-b28ecee6090d.png)

となり、切り替わっていることがわかります。

凝ったフォームの生成は厳しいですが、簡単なフォームならサーバー側の修正だけでお手軽にバリエーションを増やせそうです。