# FormKit Schema
參考文件:
- [FormKit Schema](https://formkit.com/advanced/schema)
雖然 Schema 是為生成表單而創建,但他能夠生成任何 HTML tag 或第三方 component
Schema 是由 "schema nodes" 組成的物件陣列,每個物件代表一個 HTML element、component 或純文字節點。當傳入單純的字串時會產生文字,而 HTML element 和 component 由兩種物件定義(分別為 `$el` 和 `$cmp` 定義)
## Schema node 語法
### 渲染任意 HTML Element
##### 欄位
- $el:可以帶入任意合法的 HTML tag,將會被渲染為 HTML element
- attrs:透過 attrs 物件傳入任意 attribute
- children:HTML 的實際內容
[live example](https://formkit.link/b31a8396f6c6cd24b58ee93132155949)
```json
[
{
$el: 'div', // 試著改成 h1 看看
attrs: {
style: { color: 'red' },
'data-foo': 'bar'
},
children: 'Hello world'
}
]
```
---
### gbtFloatingLabelTextInput
說明:文字輸入框,但當使用者互動時會有label 漂浮的 動畫效果
##### 欄位
- $formkit:對應的 FormKit 輸入類型
- name:FormKit 蒐集輸入資料 JSON 的 key
- <font color="red">label</font>:必須帶入,漂浮用的 placeholder 文字
- <font color="red">placeholder</font>:不可帶入,避免和漂浮的 label 重疊
- inputType:最終會渲染上的 type,ex: `<input type="email">`
- validation:驗證規則
- validationMessages:驗證錯誤訊息
##### 範例
```json
{
"$formkit": "gbtFloatingLabelTextInput",
"name": "Firstname",
"label": "Firstname",
"inputType": "email",
"validation": [
[
"required",
"trim"
],
[
"matches",
"johnson"
]
],
"validationMessages": {
"required": "此欄位必填"
},
}
```
---
### gbtMultiSelect
說明:下拉選單輸入項,分為**多選**和**單選**兩種類型
##### 欄位
- $formkit:對應的 FormKit 輸入類型
- name:FormKit 蒐集輸入資料 JSON 的 key
- <font color="red">options</font>:下拉選單選項清單
- 結構:`Array<{ value: string, label: string }>`
- value:FormKit 蒐集到 JSON 輸出值 (不一定要命名為 value,但須和 `vueMultiSelectOptions.trackBy` 選項設定一致)
- label:顯示在下拉選單中的文字 (不一定要命名為 label,但須和 `vueMultiSelectOptions.label` 選項設定一致)
- <font color="red">vueMultiSelectOptions</font>:轉接給 [Vue-Multiselect](https://vue-multiselect.js.org/#sub-option-groups) 套件使用的選項
- trackBy:指定使用 option 物件的哪個 key 值作為 FormKit 輸出值
- label:指定使用 option 物件的哪個 key 值作為顯示的文字
- placeholder
- multiple:是否可多選
- validation:驗證規則
- validationMessages:驗證錯誤訊息
##### 範例
```json
{
"$formkit": "gbtMultiSelect",
"name": "officeLocation",
"options": [
{
"value": "1",
"label": "office 1"
},
{
"value": "2",
"label": "office 2"
},
{
"value": "3",
"label": "office 3"
},
{
"value": "4",
"label": "office 4"
},
{
"value": "5",
"label": "office 5"
}
],
"vueMultiSelectOptions": {
"trackBy": "value",
"label": "label",
"placeholder": "Office Location",
"multiple": true
},
"validation": "required",
"validationMessages": {
"required": "此欄位必填"
}
}
```
---
### gbtCheckbox
說明:checkbox 輸入項,分為**多選**和**單選**兩種類型
#### 單選
##### 欄位
- $formkit:對應的 FormKit 輸入類型
- name:FormKit 蒐集輸入資料 JSON 的 key
- <font color="red">label</font>:顯示在 checkbox 右側的文字
- validation:驗證規則
- validationMessages:驗證錯誤訊息
>Note:單選 checkbox 的輸出資料型別是 Boolean
##### 範例
```json
{
"$formkit": "gbtCheckbox",
"name": "SingleCheckbox",
"label": "Single Checkbox",
"validation": [
[
"required"
]
],
"validationMessages": {
"required": "此欄位必填"
}
}
// Output
{
"SingleCheckbox": true
}
```
#### 多選
##### 欄位
- $formkit:對應的 FormKit 輸入類型
- name:FormKit 蒐集輸入資料 JSON 的 key
- <font color="red">allowOther</font>:在所有選項後方額外顯示 "other" 的選項,並產生文字輸入框給使用者填寫
- <font color="red">options</font>:checkbox 選項清單
- 結構:`Array<{ value: string, label: string }>`
- value:FormKit 蒐集到 JSON 輸出值
- label:顯示在 checkbox 右側的文字
- 注意事項:
- 由於目前 other 的文字輸入框的字串值會直接收到陣列底下,為做區分,目前需在一般 checkbox 的 value 前加入 prefix
- validation:驗證規則
- 目前當 `allowOther` 為 true 時,需額外帶入 `optionAllowOther` 規則使 other 選項的文字輸入框必填,可再討論是否做成固定規則
- validationMessages:驗證錯誤訊息
##### 範例
```json
{
"$formkit": "gbtCheckbox",
"name": "Inquiries",
"allowOther": true,
"options": [
{
"value": "gbtCheckbox-1",
"label": "Request for Quotation"
},
{
"value": "gbtCheckbox-2",
"label": "Form a Partnership"
},
{
"value": "gbtCheckbox-3",
"label": "Talk to Sales"
},
{
"value": "gbtCheckbox-4",
"label": "Educational Resource"
},
{
"value": "gbtCheckbox-5",
"label": "Need More Information"
},
{
"value": "gbtCheckbox-6",
"label": "After-sales Service"
}
],
"validation": [
[
"required"
],
[
"optionAllowOther"
]
],
"validationMessages": {
"optionAllowOther": "Please provide a value for other."
}
}
// Output
{
"Inquiries": [
"gbtCheckbox-2",
"gbtCheckbox-1",
"gbtCheckbox-3",
"gbtCheckbox-4",
"1234" // 這是 other 文字輸入框填寫的值
]
}
```
---
## 條件渲染
FormKit schema 可以利用 references 和 expression 讓 schema nodes 和 attributes 根據條件判斷顯示,條件可透過兩種方式撰寫
1. 在 `$el` 和 `$cmp 上加 if 屬性
2. 在物件上加入 `if/then/else`
### `if` 屬性
在 `$el` 和 `$cmp` 上加入 `if` 屬性就大致就像 `v-if` 一樣,當條件為 truthy 就會被渲染
範例:[live example](https://formkit.link/176f4d6012faf01e451be85490ac0860)
```javascript
<script setup>
import { reactive } from 'vue'
const data = reactive({ value: 0 })
const schema = [
{
$el: 'h2',
if: '$value >= 5 && $value <= 10',
children: '$value + " is between 5-10!"',
}
]
</script>
<template>
<FormKit
type="number"
v-model="data.value"
label="Enter a number between 5-10"
/>
<FormKitSchema
:schema="schema"
:data="data"
/>
</template>
```
### `if/then/else` 物件
可用來處理更複雜的邏輯,依據條件渲染 schema node、attrs 屬性等等,並且可以巢狀使用
範例:
1. 用於 schema nodes [連結](https://formkit.link/6727a421e5d44fffb673cf427860171e)
2. 用於 attrs 和 props [連結](https://formkit.link/80b0046e93e37bcc59dc819e7b96dbcb)
3. 目前 survey 欄位排版以及下拉選單連動其他選項的寫法 [本機 Demo 網址](http://10.1.4.221:5173/)
```json
{
"$el": "div",
"attrs": {
"class": "gc-form-group"
},
"children": [
{
"$el": "div",
"children": "About Your Inquiry",
"attrs": {
"class": "gc-form-group-title"
}
},
{
"$el": "div",
"attrs": {
"class": "gc-form-row"
},
"children": [
{
"$el": "div",
"attrs": {
"class": "col"
},
"children": [
{
"$formkit": "gbtMultiSelect",
"name": "productInterest",
"id": "productInterest",
"validation": "required",
"options": [
{
"value": "schema",
"label": "schema"
},
{
"value": "custom",
"label": "custom"
},
{
"value": "awesome",
"label": "awesome"
},
{
"value": "other",
"label": "other"
}
],
"validationMessages": {
"required": "此欄位必填"
},
"vueMultiSelectOptions": {
"trackBy": "value",
"label": "label",
"placeholder": "Product you’re interested in*"
}
}
]
},
{
"if": "$get(productInterest).value === schema",
"$el": "div",
"attrs": {
"class": "col"
},
"children": [
{
"key": "productInterest.schema",
"name": "productUsedIn",
"validation": "required",
"options": [
{
"id": "schema 1",
"displayLabel": "schema 1"
},
{
"id": "schema 2",
"displayLabel": "schema 2"
},
{
"id": "schema 3",
"displayLabel": "schema 3"
}
],
"vueMultiSelectOptions": {
"placeholder": "You are using the product for*",
"trackBy": "id",
"label": "displayLabel"
},
"validationMessages": {
"required": "此欄位必填22"
},
"$formkit": "gbtMultiSelect"
}
]
},
{
"if": "$get(productInterest).value === custom",
"$el": "div",
"attrs": {
"class": "col"
},
"children": [
{
"key": "productInterest.custom",
"name": "productUsedIn",
"validation": "required",
"options": [
{
"id": "1",
"displayLabel": "custom 1"
},
{
"id": "2",
"displayLabel": "custom 2"
},
{
"id": "3",
"displayLabel": "custom 3"
}
],
"vueMultiSelectOptions": {
"placeholder": "You are using the product for*",
"trackBy": "id",
"label": "displayLabel"
},
"validationMessages": {
"required": "此欄位必填"
},
"$formkit": "gbtMultiSelect"
}
]
},
{
"if": "$get(productInterest).value === other",
"$el": "div",
"attrs": {
"class": "col"
},
"children": [
{
"key": "productInterest.other",
"name": "productUsedIn",
"$formkit": "text",
"placeholder": "test text placeholder"
}
]
}
]
},
{
"$el": "div",
"attrs": {
"class": "gc-form-row"
},
"children": [
{
"$el": "div",
"attrs": {
"class": "col"
},
"children": [
{
"name": "textarea",
"placeholder": "Help us better understand your inquiry (limited to 1000 characters)*",
"maxlength": 10,
"validation": "required:trim",
"$formkit": "textarea"
}
]
}
]
}
]
}
```
## Survey 項目使用的 JSON 結構
```json
[
{
"$el": "div",
"attrs": {
"class": "gc-form-group",
"data-group-Id": "group-1111"
},
"children": [
{
"$el": "div",
"children": "Inquiries*",
"attrs": {
"class": "gc-form-group-title"
}
},
{
"$el": "div",
"attrs": {
"class": "gc-form-row"
},
"children": [
{
"$el": "div",
"attrs": {
"class": "col",
"data-col-id": "col-1111"
},
"children": [
{
"$formkit": "gbtCheckbox",
"name": "Inquiries",
"allowOther": true,
"options": [
{
"value": "gbtCheckbox-1",
"label": "Request for Quotation"
},
{
"value": "gbtCheckbox-2",
"label": "Form a Partnership"
},
{
"value": "gbtCheckbox-3",
"label": "Talk to Sales"
},
{
"value": "gbtCheckbox-4",
"label": "Educational Resource"
},
{
"value": "gbtCheckbox-5",
"label": "Need More Information"
},
{
"value": "gbtCheckbox-6",
"label": "After-sales Service"
}
],
"validation": [
[
"required"
],
[
"optionAllowOther"
]
],
"validationMessages": {
"optionAllowOther": "Please provide a value for other."
}
}
]
}
]
},
{
"$el": "div",
"attrs": {
"class": "gc-form-row"
},
"children": [
{
"$el": "div",
"attrs": {
"class": "col"
},
"children": [
{
"$formkit": "gbtCheckbox",
"name": "SingleCheckbox",
"label": "Single Checkbox"
}
]
}
]
}
]
},
{
"$el": "div",
"attrs": {
"class": "gc-form-group"
},
"children": [
{
"$el": "div",
"children": "Profile",
"attrs": {
"class": "gc-form-group-title"
}
},
{
"$el": "div",
"attrs": {
"class": "gc-form-row"
},
"children": [
{
"$el": "div",
"attrs": {
"class": "col"
},
"children": [
{
"name": "Firstname",
"label": "Firstname",
"validation": [
[
"required",
"trim"
],
[
"matches",
"johnson"
]
],
"help": "Label 會漂浮的文字框",
"$formkit": "gbtFloatingLabelTextInput"
}
]
},
{
"$el": "div",
"attrs": {
"class": "col"
},
"children": [
{
"name": "Lastname",
"placeholder": "Lastname",
"validation": [
[
"required",
"trim"
]
],
"$formkit": "text"
}
]
}
]
},
{
"$el": "div",
"attrs": {
"class": "gc-form-row"
},
"children": [
{
"$el": "div",
"attrs": {
"class": "col"
},
"children": [
{
"formkitInputType": "gbtFloatingLabelTextInput",
"inputType": "email",
"name": "email",
"label": "Work Email",
"validation": "required:trim|email",
"help": "To ensure our emails be delivered to your inbox, please avoid using free-to-use email services such as Yahoo, Gmail, Hotmail etc.",
"$formkit": "gbtFloatingLabelTextInput"
}
]
}
]
}
]
},
{
"$el": "div",
"attrs": {
"class": "gc-form-group"
},
"children": [
{
"$el": "div",
"children": "Company",
"attrs": {
"class": "gc-form-group-title"
}
},
{
"$el": "div",
"attrs": {
"class": "gc-form-row"
},
"children": [
{
"$el": "div",
"attrs": {
"class": "col"
},
"children": [
{
"name": "Company",
"id": "Company_123",
"placeholder": "Company",
"validation": "required:trim",
"$formkit": "text"
}
]
},
{
"if": "$get(Company_123).value",
"$el": "div",
"attrs": {
"class": "col"
},
"children": [
{
"name": "URL",
"placeholder": "URL",
"validation": "required:trim",
"$formkit": "text"
}
]
}
]
},
{
"$el": "div",
"attrs": {
"class": "gc-form-row"
},
"children": [
{
"$el": "div",
"attrs": {
"class": "col"
},
"children": [
{
"$formkit": "gbtMultiSelect",
"name": "officeLocation",
"options": [
{
"value": "office 1",
"label": "office 1"
},
{
"value": "office 2",
"label": "office 2"
},
{
"value": "office 3",
"label": "office 3"
},
{
"value": "office 4",
"label": "office 4"
},
{
"value": "office 5",
"label": "office 5"
}
],
"vueMultiSelectOptions": {
"trackBy": "value",
"label": "label",
"placeholder": "Office Location",
"multiple": true
},
"validation": "required",
"validationMessages": {
"required": "此欄位必填"
}
}
]
}
]
}
]
},
{
"$el": "div",
"attrs": {
"class": "gc-form-group"
},
"children": [
{
"$el": "div",
"children": "About Your Inquiry",
"attrs": {
"class": "gc-form-group-title"
}
},
{
"$el": "div",
"attrs": {
"class": "gc-form-row"
},
"children": [
{
"$el": "div",
"attrs": {
"class": "col"
},
"children": [
{
"$formkit": "gbtMultiSelect",
"name": "productInterest",
"id": "productInterest",
"validation": "required",
"options": [
{
"value": "schema",
"label": "schema"
},
{
"value": "custom",
"label": "custom"
},
{
"value": "awesome",
"label": "awesome"
},
{
"value": "other",
"label": "other"
}
],
"validationMessages": {
"required": "此欄位必填"
},
"vueMultiSelectOptions": {
"trackBy": "value",
"label": "label",
"placeholder": "Product you’re interested in*"
}
}
]
},
{
"if": "$get(productInterest).value === schema",
"$el": "div",
"attrs": {
"class": "col"
},
"children": [
{
"key": "productInterest.schema",
"name": "productUsedIn",
"validation": "required",
"options": [
{
"id": "schema 1",
"displayLabel": "schema 1"
},
{
"id": "schema 2",
"displayLabel": "schema 2"
},
{
"id": "schema 3",
"displayLabel": "schema 3"
}
],
"vueMultiSelectOptions": {
"placeholder": "You are using the product for*",
"trackBy": "id",
"label": "displayLabel"
},
"validationMessages": {
"required": "此欄位必填22"
},
"$formkit": "gbtMultiSelect"
}
]
},
{
"if": "$get(productInterest).value === custom",
"$el": "div",
"attrs": {
"class": "col"
},
"children": [
{
"key": "productInterest.custom",
"name": "productUsedIn",
"validation": "required",
"options": [
{
"id": "1",
"displayLabel": "custom 1"
},
{
"id": "2",
"displayLabel": "custom 2"
},
{
"id": "3",
"displayLabel": "custom 3"
}
],
"vueMultiSelectOptions": {
"placeholder": "You are using the product for*",
"trackBy": "id",
"label": "displayLabel"
},
"validationMessages": {
"required": "此欄位必填"
},
"$formkit": "gbtMultiSelect"
}
]
},
{
"if": "$get(productInterest).value === other",
"$el": "div",
"attrs": {
"class": "col"
},
"children": [
{
"key": "productInterest.other",
"name": "productUsedIn",
"$formkit": "text",
"placeholder": "test text placeholder"
}
]
}
]
},
{
"$el": "div",
"attrs": {
"class": "gc-form-row"
},
"children": [
{
"$el": "div",
"attrs": {
"class": "col"
},
"children": [
{
"name": "textarea",
"placeholder": "Help us better understand your inquiry (limited to 1000 characters)*",
"maxlength": 10,
"validation": "required:trim",
"$formkit": "textarea"
}
]
}
]
}
]
},
{
"$el": "div",
"attrs": {
"class": "privacy-policy",
"innerHTML": "<div href=\"haha\">We will process your personal information in accordance with our <a href=\"hello\">Privacy Policy</a> and you may unsubscribe at any time.</div>"
}
}
]
```