學習六角學院「Vue 出一個電商網站」課程時,課程後半段是跟著老師的示範,實作出電商網站的完整功能。
這篇筆記便是紀錄了當時實作的部分功能:Navbar 中的購物車列表、商品頁側欄的商品分類。
製作 Navbar 上的購物車
步驟類似上一篇最後一段「使用 Actions 取得遠端資料」。首先將「取得購物車資料」(getCart()
) 及「刪除購物車品項」(removeCart()
) 的 AJAX 寫到 store 中的 actions
中,購物車資料結構也從元件 data
搬到 store 中的 state
。
整理程式碼,例如把 AJAX 的 http
方法改成 axios
,帶入 context
參數,以及刪除購物車品項後重新取得購物車清單的程式碼,要改成以下這段:
1
| context.dispatch('getCart');
|
- 回到 App.vue,原本的
methods
程式碼改成呼叫 store 中的 actions
,且 computed
也要呼叫 state
中的資料。
1 2 3 4 5 6
| getCart() { this.$store.dispatch('getCart', ); }, removeCart(id) { this.$store.dispatch('removeCart', id); }
|
1 2 3 4 5 6
| computed: { ... cart() { return this.$store.state.cart; } },
|
把「加入購物車」的功能加進來。到 Home.vue,複製 addtoCart()
的程式碼,貼到 actions
中。一樣要整理程式碼,用 dispatch()
呼叫 getCart()
。
addtoCart()
因為會從 Home.vue 傳遞兩個參數到 actions
的關係,所以就帶出一個問題:actions
中的函式一次只能從外部接收一個參數。解決方式就是在 Home.vue 的 addtoCart()
中用物件把兩個參數包起來,而 actions
中的 addtoCart()
傳入的參數也用物件包起來。
1 2 3 4 5 6 7 8 9
| addtoCart(id, qty = 1) { this.$store.dispatch('addtoCart', {id, qty}); }
addtoCart(context, {id, qty}) { ... }
|
製作側欄商品分類的功能
前置作業
- 已經在 store 處理好商品分類有哪些。
1 2 3 4 5 6 7 8 9
| CATEGORIES(state, payload) { const categories = new Set(); payload.forEach(item => { categories.add(item.category); }); state.categories = Array.from(categories); },
|
在元件中,已經用 computed
從 store 取回商品資料與分類資料。
元件模板中有搜尋欄,並綁定 v-model
以取得搜尋欄中的文字。
側欄分類連結
- 商品分類用
v-for
從取回來的商品資料疊代。
- 點擊該分類時,搜尋欄文字要等於這個分類。
- 當搜尋欄文字等於這個分類時,class 為啟用狀態。
- 按下「全部顯示」這個分類時,搜尋文字清空,且當搜尋文字為空時,class 為啟用狀態。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <div class="list-group sticky-top"> <a class="list-group-item list-group-item-action" href="#" @click.prevent="searchText = item" :class="{ active: item === searchText }" v-for="item in categories" :key="item"> <i class="fa fa-street-view" aria-hidden="true"></i> {{ item }} </a> <a href="#" class="list-group-item list-group-item-action" @click.prevent="searchText = ''" :class="{ active: searchText === '' }"> 全部顯示 </a> </div>
|
商品過濾的邏輯
在 computed 中新增 filterData()
,在這裏比對商品資料的「分類」與 data
定義的「搜尋欄文字」是否符合,並 return
符合搜尋欄文字的商品。
1 2 3 4 5 6 7 8 9 10 11 12 13
| filterData() { const vm = this; if (vm.searchText) { return vm.products.filter(item => { const data = item.category .toLowerCase() .includes(vm.searchText.toLowerCase()); return data; }); } return this.products; },
|
子畫面呈現過濾後的商品列表
商品卡片最外層用 .col-md-4
包住,並且在這一層用 v-for
從 computed
filterData()
中疊代出過濾後的每一項商品。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <div class="col-md-4 mb-4" v-for="item in filterData" :key="item.id"> <div class="card border-0 box-shadow text-center h-100"> <img class="card-img-top" :src="item.image" alt="Card image cap" /> <div class="card-body"> <h4 class="card-title">{{ item.title }}</h4> <p class="card-text text-left">{{ item.content }}</p> </div> <div class="card-footer border-top-0 bg-white"> <button class="btn btn-outline-secondary btn-block btn-sm" @click="addtoCart(item.id)"> <i class="fa fa-cart-plus" aria-hidden="true"></i> 加到購物車 </button> </div> </div> </div>
|