# 08. 互動式資料表單

像選擇縣市地區的資料連動選單:

# 摘要

  1. 改變資料結構:將依天排序的資料,改為依類型排序的巢狀結構。
  2. 產生第一層選單 (type):顯示所有類別。
  3. 產生第二層選單 (title):依據選擇的類別列出標題。
  4. 在下方顯示篩選後文字內容:依據選擇的標題顯示其內容。

# 改變資料結構

這個單元的 input 用來進行巢狀資料篩選。原本的資料是以天排列的,因此要先改變資料結構,讓資料成為如下的巢狀結構:

類型 (type)
  └ 標題 (title)
      ├ 連結 (link)
      └ 天數 (index)

首先,資料結構的改變使用 Vue 的computed

typeList() {
  let obj = {
    sort: [], // 儲存排序。
    map: {}   // 儲存對應表 (每個 type 裡面對應的內容)。
  }
  this.menu.forEach((item, index) => {
    let {type, title, link} = item
    // 將沒出現過的 type 初始化,以便後面塞資料。
    if (!obj.map[type]) {
      obj.sort.push(type)
      obj.map[type] = {
        sort: [],
        map: {}
      }
    }
    // 將原本的資料放到對應的新位置
    obj.map[type].sort.push(title)
    obj.map[type].map[title] = { index, link };
  })
  return obj
},

複習:forEach (opens new window)

# 產生第一層選單 (type)

在 Vue data 建立 input ,裡面的 typetitle 預設值為 null

data = {
    input: {
        type: null,
        title: null
    }
}

然後將 typeList 的資料綁定到選單。

<select v-model="input.type">
    <!-- 這裡的 null 因為被綁定,因此被當作空的程式 -->
    <option :value="null">請選擇</option>
    <option v-for="item in typeList.sort">{{ item }}</option>
</select>

# 產生第二層選單 (title)

當我們從 input 選擇了一個 type ,要回傳 typeList 中對應的 title 清單,沒有的話就回傳空陣列。透過 computed 來產生第二層資料的清單:

titleList() {
  this.input.title = null;  // 先清空 input.title 的值
  if (this.input.type) {
    return this.typeList.map[this.input.type];
  } else {
    return [];
  }
},

要注意的是,假設先選了 「風味沙拉」 和裡面的 「CSS 與 jQuery 動畫」,再重新去選「用餐插曲」,會發現 title 選單變成一片空白。這是因為剛才選的 input.title 的值還在,但是新的 input.type 裡面沒有這個值,才導致 bug 出現。

解決方式是,每次選擇 input.type 時,都先將 input.title 的值清除,讓其值為 null ,對應 HTML 裡的 「請選擇」 選項。

# 在下方顯示篩選後文字內容

最後,依據選擇的 input.title 顯示天數跟連結。

content() {
  if (this.input.title) {
    return this.titleList.map[this.input.title];
  } else {
    return null;
  }
}

本範例 HTML 的主要結構則如下:

<div class="input">
  <select v-model="input.type">
    <!-- 這裡的 null 因為被綁定,因此被當作空的程式 -->
    <option :value="null">請選擇</option>
    <option v-for="item in typeList.sort">{{ item }}</option>
  </select>
  <select style="width: 500px" v-model="input.title">
    <option :value="null">請選擇</option>
    <option v-for="item in titleList.sort">{{ item }}</option>
  </select>
</div>

<div class="description">
  <div class="menuItem" style="text-align: center" v-if="content">
    <span class="number">{{ content.index + 1 }}</span>
    <a :href="content.link" class="title">{{ content.link }}</a>
  </div>
</div>

這裡的 null 因為被綁定,因此被當作空的程式。當 input.typeinput.title 的值為 null 的時候,顯示的選項會是「請選擇」。