iT邦幫忙

2021 iThome 鐵人賽

DAY 26
0
Modern Web

我的Vue學習筆記系列 第 26

Day26-實作(列表區) (part1)

要對個別的元件做更多的指令之前,需要先把他們個別的拆出來,第一個拆解的是列表的部分,先在components/下建立一個asideMenu.vue檔案,把剛剛App.vue中的<div class="aside-menu">...</div>搬過去

//asideMenu.vue

<template>
  <div class="aside-menu">
    ...
  </div>
</template>

<script>
	export default {
	  name: "asideMenu",
	};
</script>

在App.vue被搬走的位置改由取代,並import新建立的

//App.vue

<template>
  <div id="app">
    <!-- aside-menu 左側欄 -->
    <asideMenu />

    <!-- 地圖區塊 -->
    <div class="mask-map" id="mask-map"></div>
  </div>
</template>

<script>
import asideMenu from "./components/asideMenu.vue";

export default {
  name: "App",
  components: {
    asideMenu,
  },
};
</script>

<style lang="scss" src="./style.scss"></style>

取得縣市及行政區資料

在store/index.js中先定義好幾個狀態

state: {
  //目前所選縣市,預設城市
  currCity: '臺北市',
  //目前所選行政區,預設北投區
  currDistrict: '北投區',
  //存放API回傳的縣市/行政區列表資訊
  location: [],
  //存放API回傳的所有藥局
  stores: []
}

先建立對應的mutations,透過他操作state的資料

mutations: {
  setcurrCity(state, payload) {
    state.currCity = payload
  },
  setcurrDistrict(state, payload) {
    state.currDistrict = payload
  },
  setAreaLocation(state, payload) {
    state.location = payload
  },
  setStores(state, payload) {
    state.stores= payload
  }
}

接著透過actions從json檔案抓取資料,存到state裡面

actions: {
  // 取得行政區資料
  async fetchLocations({ commit }) {
    const json = await fetch('https://raw.githubusercontent.com/kurotanshi/mask-map/master/raw/area-location.json')
      .then((res) => res.json())
	//commit操作mutaions
    commit('setAreaLocation', json)
  },
  // 取得藥局資料
  async fetchPharmacies({ commit }) {
    const json = await fetch('https://raw.githubusercontent.com/kiang/pharmacies/master/json/points.json')
      .then((res) => res.json())
		//整理資料格式,拆出經緯度
    const data = json.features.map((d) => ({
      ...d.properties,
      latitude: d.geometry.coordinates[0],
      longitude: d.geometry.coordinates[1]
    }))
		//commit操作mutaions
    commit('setStores', data)
  }
}

最後回到App.vue新增mountedhook

import { mapActions } from "vuex";
import asideMenu from "./components/asideMenu.vue";

export default {
  name: "App",
  components: {
    asideMenu,
  },
  methods: {
    ...mapActions(["fetchLocations", "fetchPharmacies"]),
  },
  mounted() {
    this.fetchLocations();
    this.fetchPharmacies();
  },
};

可以從開發者工具看看是否有發送遠端請求

Untitled

將行政區套用至左側選單

得到資料後就要把它放到左側兩個下拉選單之中,在asideMenu.vue下新增computed來存取state的資料,將currCity和currDistrict透過v-model雙向綁定。

computed: {
  currCity: {
    get() {
      return this.$store.state.currCity;
    },
    set(value) {
      this.$store.commit("setcurrCity", value);
    },
  },
  currDistrict: {
    get() {
      return this.$store.state.currDistrict;
    },
    set(value) {
      this.$store.commit("setcurrDistrict", value);
    },
  },
},

並改寫模板

<div class="aside-menu">
  <div class="wraps">
    <label>
      縣市:<select v-model="currCity">
        <option>臺北市</option>
      </select>
    </label>
    <label>
      行政區:<select v-model="currDistrict">
        <option>北投區</option>
      </select>
    </label>
  </div>

接下來要處理option中的資料,從state回傳的資料如下,並不適合用v-for渲染,所以需要回到getters處理

▼ location:Array[21]
	▼ 0:
		▶ districts: Array[12]
			 id: "0000"
			 name: "臺北市"
			 sort: 1
	▼ 1:
	▶ districts: Array[29]
		 id: "0001"
		 name: "新北市"
		 sort: 2

行政區的部分有使用到optional chaining處理預設問題,關於此用法可以參考此連結

getters: {
  cityList(state) {
    //城市
    return state.location.map((d) => d.name)
  },
  districtList(state) {
    //行政區
    return state.location.find((d) => d.name === state.currCity)?.districts || []
  }
}

處理好資料後回到asideMenu.vue在computed新增mapGetters

computed: {
  currCity: {...},
  currDistrict: {...},
  ...mapGetters(["cityList", "districtList"]),
},

並再次改寫option模板的部分

<div class="aside-menu">
  <div class="wraps">
    <label>
      縣市:<select v-model="currCity">
        <option v-for="c in cityList" :key="c">{{ c }}</option>
      </select>
    </label>
    <label>
      行政區:<select v-model="currDistrict">
        <option v-for="d in districtList" :key="d.id">{{ d.name }}</option>
      </select>
    </label>
  </div>

這樣大致就完成囉!!! 最後,可以加入一個watch功能,讓使用者在選擇新的城市時,行政區會自動切換第一個行政區。districtList 一更新,this.currDistrict也會馬上切換成第一筆資料。

watch: {
  districtList(v) {
    const [arr] = v;
    this.currDistrict = arr.name;
  },
},

明天繼續完成剩下的部分...


上一篇
Day25-實作
下一篇
Day27-實作(列表區) (part2)
系列文
我的Vue學習筆記30

尚未有邦友留言

立即登入留言