Vue 初心者筆記 #21 元件變換(上)

本篇文章單純是紀錄一下自己製作六角 Vue 課程,元件章節的作業的過程與邏輯。

將資料內容透過 “元件” 呈現

STEP 1:使用 v-for 製作多個元件

  • 使用 props 傳遞外層的資料到內層
  • 記得加上一個不重複的值作為 :key
1
2
<city class="card" v-for="(item, key) in data" :key="item.SiteName" :cities='item'>
</city>

STEP 2:將 Card 元件以 X-Template 呈現

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script type="text/x-template" id="city">
<div class="card status-aqi2">
<div class="card-header">{{ cities["County"] }} - {{ cities["SiteName"] }}
<a href="#" class="float-right"><i class="far fa-star"></i></a>
</div>
<div class="card-body">
<ul class="list-unstyled">
<li>AQI 指數: {{ cities["AQI"] }} </li>
<li>PM2.5: {{ cities["PM2.5"] }}</li>
<li>說明: {{ cities["Status"] }}</li>
</ul>
{{ cities["PublishTime"] }}
</div>
</div>
</script>

製作城市過濾選項

STEP 1:製作不重複的選單選項

這邊有參考之前高雄旅遊網作業的寫法。

  • 使用 Array.prototype.push() 方法
  • 使用 Set() + Array.from() 取出陣列的不重複值
1
2
3
4
5
let origin = [];
for (let i = 0; i < vm.data.length; i++) {
origin.push(vm.data[i].County);
}
vm.location = Array.from(new Set(origin));

參考資料:JavaScript 取出陣列重複 / 不重複值的方法 | Gua’s Note

STEP 2:繪製元件時加入 v-if 判斷式

剛載入畫面、沒選擇時(selected == ''),會顯示全部縣市的元件。
有選擇後(item.County == selected),就會顯示該縣市的卡片元件。

1
2
3
<city class="card" v-for="(item, key) in data" :key="item.SiteName" :cities='item'
v-if="selected == '' || item.County == selected">
</city>

製作 “關注城市” 功能

STEP 1:增加關注城市

  • 將內層資料透過 $emit 傳遞到外層

詳細步驟:

  1. 點擊卡片元件觸發 addToStared 事件。
  2. 使用 $emit 觸發實體上自訂的 update-stared 事件。
  3. 觸發 updateStared(item) 事件,即可增加關注的城市。
1
2
3
4
5
6
7
8
updateStared: function(item){
const vm = this;
// Array.prototype.includes() 方法
// 判斷陣列是否包含特定的元素,回傳 true 或 false。
if(!vm.stared.includes(item)){
vm.stared.push(item);
}
}

STEP 2:加上星號

  • 使用插槽(這裡使用具名插槽),將已關注的城市加上星號
  • 插槽內的圖示換為實心星星 <i class="fas fa-star"></i>
  • 使用 v-for 渲染出 stared 陣列裡的資料
1
2
3
4
5
6
<city class="card" v-for="(item, key) in stared" :key="item.SiteName" :cities='item'>
<!-- 插槽 -->
<template slot="star">
<a href="#" class="float-right" @click.prevent="cancelStared(item)"><i class="fas fa-star"></i></a>
</template>
</city>

STEP 3:移除關注城市

  • 如上段程式碼,使用 v-on:click.prevent 觸發 cancelStared(item) 事件
  • 使用 splice() 方法
1
2
3
4
5
6
7
8
9
cancelStared: function(item){
const vm = this;
for(let i=0; i<=vm.stared.length; i++){
if(vm.stared[i].SiteName == item.SiteName){
vm.stared.splice(i, 1);
break;
}
}
}

依據不同污染呈現不同色彩

X-Template 中加入 v-bind 來動態加入 Class 名稱。

“判斷式” 可使用「陣列」的方式,加入多個條件式,以判定不同的汙染程度所對應的色彩。

1
2
3
4
5
6
7
8
9
10
11
<script type="text/x-template" id="city">
<!-- 依據不同污染呈現不同色彩 -->
<div class="card"
:class="[{'status-aqi1': cities['Status']=='良好'},
{'status-aqi2': cities['Status']=='普通'},
{'status-aqi3': cities['Status']=='對敏感族群不健康'},
{'status-aqi4': cities['Status']=='對所有族群不健康'},
{'status-aqi5': cities['Status']=='非常不健康'},
{'status-aqi6': cities['Status']=='危害'}]">

......

加分題:透過 LocalStorage 儲存上次關注的城市

做法是當我們新增或移除關注城市時,同步將 stared 陣列的結果更新到 LocalStorage 上,達到儲存上次關注城市的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
updateStared: function(item){
const vm = this;
if(!vm.stared.includes(item)){
vm.stared.push(item);
}
// 更新 LocalStorage
localStorage.setItem('listData', JSON.stringify(vm.stared));
},
cancelStared: function(item){
const vm = this;
for(let i=0; i<=vm.stared.length; i++){
if(vm.stared[i].SiteName == item.SiteName){
vm.stared.splice(i, 1);
break;
}
}
// 更新 LocalStorage
localStorage.setItem('listData', JSON.stringify(vm.stared));
}

成品範例程式碼:

優化:LocalStorage 只儲存單個項目

最後,這個作業還要做一個小小的修正,就是將 LocalStorage 改為只儲存單個項目。
如果儲存整個資料,會造成遠端與本機資料因資料太大,而無法即時更新,而會有出現誤差的可能性。

要將 LocalStorage 改為只儲存單個項目,本人是以下列方式修改:

  1. 只把 item.SiteName 存入 LocalStorage
  2. 透過 v-if 判斷 stared 陣列裡是否包含 item.SiteName,如果有的話,就將這個 item 呈現在關注城市內。

修正後的範例程式碼:

以上資源是我自己整理過後的筆記,若有錯誤歡迎隨時和我聯繫。