---
tags: JS 5組 - 2022 秋季 JS直播班, 最終關卡挑戰 Day2 - 前台(購物車 - 功能整合)
title: 最終關卡挑戰 Day2 - 前台(購物車 - 功能整合)
---
###### tags: `JS 5組 - 2022 秋季 JS直播班` 、`最終關卡挑戰 Day2 - 前台(購物車 - 功能整合)`
###### *date: 2022 / 11/ 22*
# Day 2 - 前台(購物車- 功能整合)
昨天我們已經完成取得商品與購物車列表資訊,今天我們將專注於把產品新增到購物車內與整合購物車功能。
### 今日挑戰目標: 前台(新增產品、修改產品數量、單筆刪除、全部刪除)

### 使用API

### 購物車功能任務
- 新增產品到購物車
- 單筆刪除購物車內的產品
- 修改購物車內的單筆產品數量
- 清空購物車
- 優化使用者體驗
- 使用 [sweetalert2](https://sweetalert2.github.io/#usage) 套件,製作提示使用者訊息。
- 增加 [loading](https://loading.io/) 動畫
---
### shinyhung#3825
```javascript
```
### WeiJ#7376
實作 API 的優化處理 (關鍵字 `Debounce` & `Throttle`)
其目的是防止使用者重複按下按鈕導至送出多次 API 常會用於送出訂單 (實際上可能會影響庫存計算)
做法大概為送出 api 時再 api 回傳結果前要將原本的按鈕加上 `disabled`,讓按鈕不能按,等到回傳結果後再移除 `disabled` 狀態
我是使用 bootstrap5 來做開發,元件中有一個功能為 [spinners](https://getbootstrap.com/docs/5.2/components/spinners/#buttons)
他可以放在按鈕之中讓按鈕顯示 loading 狀態,而在非 `loadding` 時則會加上 `d-none` display:none 不顯示這個 className,所以我就用這個 className 做判斷
以商品加入購物車為例,我的 html 會是
```htmlembedded
<button class="btn btn-black btn-block" data-id="id">
加入購物車
<span class="d-none loading spinner-border spinner-border-sm" role="status"></span>
</button>
```
除了按鈕外也放入 spinner 元件,並預設為 d-none 不顯示,當我按下這個 button 時會顯示 spinner 同時不能再點選第二下,直到 spinner 消失
```javascript
const addCartBtns = document.querySelectorAll('.product__item__btn button')
addCartBtns.forEach(btn => {
btn.addEventListener('click', (e) => {
// 找到 button 下的 .loading 元素
const loading = e.target.querySelector('.loading');
// 如果 .loading 的 className 沒有 d-none 則代表 API 正在執行 return 結束函式
if (!loading.classList.contains('d-none'))
return;
const id = e.target.dataset.id;
loading.classList.remove('d-none') // 移除 d-none 讓 spinner 顯示,接著執行 API
cart.add(id);
})
})
// cart.add(id)
api.apiAddCart({
data: {
productId: id,
quantity: 1
}
}).then((res) => {
const { status, carts, finalTotal, message } = res.data;
if (status) {
// ...
loading.classList.add('d-none') // 完成時要把 d-none 加回讓按鈕變成可點
successAlert('加入購物車成功');
} else {
errorAlert(message);
}
})
```
### charlottelee849#0366
待辦事項 (未完成)
- 後台
- [ ] 觀看後台訂單
**前台知識點**
validate.js跟 mail 獨立驗證混合運用
```JavaScript
// mail 驗證放入送出訂單函式 做 if 判斷
if (validateEmail(customerEmail) == false) {
Swal.fire("請填寫正確的Email", "格式輸入錯誤", "error");
return;
}
// 獨立對 email 驗證欄位做監聽
const customerEmail = document.querySelector("#customerEmail");
customerEmail.addEventListener("blur", function (e) {
if (validateEmail(customerEmail.value) == false) {
document.querySelector(`[data-message=Email]`).textContent =
"請填寫正確 Email 格式";
return;
} else {
document.querySelector(`[data-message=Email]`).textContent =
"";
return;
}
});
// mail欄位驗證
function validateEmail(mail) {
if (
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(
mail
)
) {
return true;
}
return false;
}
```
### jimmyFang#9575
**任務清單(已完成)**
- [x] 新增產品到購物車
- [x] 單筆刪除購物車內的產品
- [x] 修改購物車內的單筆產品數量
- [x] 清空購物車
- [x] 優化使用者體驗
- 使用 [sweetalert2](https://sweetalert2.github.io/#usage) 套件,製作提示使用者訊息。
- 增加 [loading](https://loading.io/) 動畫
目前進度: [demo](https://jimmyfang-ai.github.io/js-finalWork-wowoRoom/#orderInfo)
**知識點分享:**
使用 [closest](https://hackmd.io/@w4wBc9wkR4CvPsIeEWiLbg/S1XivuMAu/%2FqUsdMyT8TcS2YEsdi0oFSw) + [dataset](https://hackmd.io/@w4wBc9wkR4CvPsIeEWiLbg/S1XivuMAu/%2FnCJeegx1QfiSnYK2TRqmAA) 取得產品列表上的 li 的 productId,因為實作上可能在產品卡片上有加入購物車、加入收藏的按鈕..等,透過這個方法可以簡化在多個按鈕上綁定 id 。
```jsx=
// 產品 - 顯示產品列表
function renderProductsList(data) {
productWrap.innerHTML = data.map((product) => {
return `<li class="productCard" data-product-id="${product.id}">
<h4 class="productType">${product.category}</h4>
<img src="${product.images}" alt="${product.title}">
<a href="#" class="addCardBtn">加入購物車</a>
<h3>${product.title}</h3>
<del class="originPrice">NT$${tothousands(product.origin_price)}</del>
<p class="nowPrice">NT$${tothousands(product.price)}</p>
</li>`
}).join('');
};
// 產品 - 新增產品到購物車
productWrap.addEventListener('click', (e) => {
e.preventDefault();
// 只有點擊到加入購物車按鈕, 就取得 li 內的 產品 id
if (e.target.getAttribute('class') !== "addCardBtn") return;
// 產品數量(預設第一次新增產品的值)
let productQty = 1;
// 產品 id
let productId = e.target.closest('li').dataset.productId;
// 比對購物車內是否存在相同產品
cartsData.forEach((cart) => {
if (cart.product.id === productId) {
productQty = cart.quantity += 1;
};
});
// 新增產品到購物車
addCartItem(productId, productQty);
});
```
### Judy Wei#6103
```JavaScript
```
### Mia小福#4473
```JavaScript
```
### Ringo#7583
```JavaScript
```
### yawun#0042
- 任務清單(已完成)
- 購物車
- [x] 新增產品到購物車
- [x] 刪除單筆購物車
- [x] 清空購物車
- [ ] 修改購物車內的單筆產品數量
- 優化工具
- [x] sweetalert2
- 知識點分享
- 使用sweetalert2的方式
1. 載入cdn: `<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.6.11/dist/sweetalert2.all.min.js"></script>`
2. 將要alert的地方改為下列這段(之後有空,再拉出成函式&重構)
```javascript=
function sweetAlert(text,icon){
Swal.fire({
text: text,
icon: icon,
confirmButtonText: 'OK'
})
}
sweetAlert('已加入購物車','success')
```
```JavaScript
```
### 法希娜#3206
->>完成:
一般商品顯示
購物車顯示+正確總額
購物車單筆刪除
全部刪除
加入購物車
送出訂單
->>待完成:輸入驗證/ 送出訂單後清空購物車
**本次新增知識點:
(一)抓取擁有同樣class但各自不同id的元素,求個別click事件的該id
```JavaScript
const addCardBtn = document.querySelectorAll('.addCardBtn');
addCardBtn.forEach((btn) =>{
btn.addEventListener('click', (e) => {
const idVal = e.target.getAttribute('id');
})
})
```