owned this note
owned this note
Published
Linked with GitHub
---
tags: JS 5組 - 2022 秋季 JS直播班, 最終關卡挑戰 Day3 - 前台(表單 - 功能整合)
title: 最終關卡挑戰 Day3 - 前台(表單 - 功能整合)
---
###### tags: `JS 5組 - 2022 秋季 JS直播班` 、`最終關卡挑戰 Day3 - 前台(表單 - 功能整合)`
###### *date: 2022 / 11/ 23*
# Day3 - 前台(表單 - 功能整合)
昨天我們已經完成購物車功能整合,今天我們將專注於表單的功能整合。
### 今日挑戰目標: 前台(表單 - 功能整合)

### 使用API

### 表單 - 功能整合任務
- 表單驗證功能(**綁定 click + change 事件**)
- 表單送出阻擋功能(購物車內產品不得為 0 )
- 整理送出購買訂單API,需要的資料格式。
- 表單送出後,需清空表單欄位的資訊。
- 優化使用者體驗
- 使用 [sweetalert2](https://sweetalert2.github.io/#usage) 套件,製作提示使用者訊息。
- 使用 [validatejs](https://validatejs.org/) 套件,製作驗證表單欄位規則。
### validatejs 套件範例
1. 使用助教的範例,有經過修改部分,格外加上 **click + change 事件**去觸發表單驗證規則。
2. 將 **洧杰老師** 的[最終任務的影音](https://courses.hexschool.com/courses/202011131/lectures/42391020)內的 email 和 電話規則的函式,整合到 **constraints 驗證規則物件內**。
<iframe height="300" style="width: 100%;" scrolling="no" title="最終作業 - validate.js" src="https://codepen.io/pohxiqqo/embed/mdKxdOo?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/pohxiqqo/pen/mdKxdOo">
最終作業 - validate.js</a> by 方振吉 (<a href="https://codepen.io/pohxiqqo">@pohxiqqo</a>)
on <a href="https://codepen.io">CodePen</a>.
</iframe>
---
### 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
待辦事項
- 前台優化
- [x] 加入 AOS 視差滾動
- [ ] 加入 Loding 效果
- 後台
- [x] 觀看後台訂單
- [ ] c3圖表
- [ ] 刪除訂單
- [ ] 狀態修改
**前台知識點**
```JavaScript
```
### jimmyFang#9575
**任務清單**
- 訂購表單(已完成)
- [x] 表單驗證功能(click + change)
- [x] 表單阻擋功能(購物車產品不得為 0 )
- [x] 整理送出購買訂單API,需要的資料格式。
- [x] 表單送出後,清空表單欄位的資訊。
- [x] 優化使用者體驗
- 使用 [sweetalert2](https://sweetalert2.github.io/#usage) 套件,製作提示使用者訊息。
- 使用 [validatejs](https://validatejs.org/) 套件,製作驗證表單欄位規則。
- 整理專案結構(未完成)
- [ ] 整理 SCSS 與 JS 結構(等JS核心的ES模組化課程)
目前進度: [demo](https://jimmyfang-ai.github.io/js-finalWork-wowoRoom/#orderInfo)
**知識點分享:**
```jsx=
```
### Judy Wei#6103
```JavaScript
```
### Mia小福#4473
```JavaScript
```
### Ringo#7583
```JavaScript
```
### yawun#0042
- 任務清單
- 購物車
- [x] 修改購物車內的單筆產品數量
- 表單
- [x] 表單驗證功能(**綁定 click + change 事件**)
- [x] 表單送出阻擋功能(購物車內產品不得為 0 )
- [x] 整理送出購買訂單API,需要的資料格式。
- [x] 表單送出後,需清空表單欄位的資訊。
- 優化使用者體驗
- [x] [sweetalert2](https://sweetalert2.github.io/#usage) 套件,製作提示使用者訊息。
- [x] [validatejs](https://validatejs.org/) 套件,製作驗證表單欄位規則。
- 待完成:[ ]優化整理JS結構,新增註解文字
- 知識點分享
:::spoiler 分享Day 2 完成的修改購物車內的單筆產品數量(patch)
1. 渲染購物車時,將購物車的數量改為input
```javascript=
cartData.forEach((item)=>{
const {id:cartId,quantity} = item; //購物車id重新命名
const {title,price,images} = item.product;
str+=`<tr>
<td>
<div class="cardItem-title">
<img src="${images}" alt="">
<p>${title}</p>
</div>
</td>
<td>NT$${toThousands(price)}</td>
<td>
<!-- 改成input,裡面藏數量和cart id -->
<input type="number" value="${quantity}" min="1" class="cartNumText" data-cart-id="${cartId}">
</td>
<td>NT$${toThousands(price*quantity)}</td>
<td class="discardBtn">
<a href="#" class="material-icons" data-discard data-cart-id="${cartId}">
clear
</a>
</td>
</tr>`
})
cartList.innerHTML = str;
```
2. 微調CSS
```css=
.cartNumText{
padding: 0 4px;
max-width: 56px;
text-align: center;
font: inherit;
font-size: 18px;
border: none;
border-bottom: 1px solid #B9B9B9;
background: #F8F8F8;
}
.cartNumText:focus{
outline: none;
border-bottom: 1px solid #000000;
}
```
3. 在渲染購物車以後,綁定購物車數量的監聽事件,以下是完整的renderCartList函式內容
```javascript=
function renderCartList(cartData,finalTotal){
const cartTotal = document.querySelector('[data-cart-total]');//總額的DOM
cartTotal.textContent = toThousands(finalTotal);//渲染總額
if(cartData.length==0){
cartList.innerHTML = '<tr><td colspan="6" class="text-center"><h3>購物車內沒有商品( ´•̥̥̥ω•̥̥̥` )</h3></td></tr>';
return
}
let str = '';
cartData.forEach((item)=>{
const {id:cartId,quantity} = item; //購物車id重新命名
const {title,price,images} = item.product;
str+=`<tr>
<td>
<div class="cardItem-title">
<img src="${images}" alt="">
<p>${title}</p>
</div>
</td>
<td>NT$${toThousands(price)}</td>
<td>
<input type="number" value="${quantity}" min="1" class="cartNumText" data-cart-id="${cartId}">
</td>
<td>NT$${toThousands(price*quantity)}</td>
<td class="discardBtn">
<a href="#" class="material-icons" data-discard data-cart-id="${cartId}">
clear
</a>
</td>
</tr>`
})
cartList.innerHTML = str;
//綁定修改購物車數量事件
const cartNumText = document.querySelectorAll('.cartNumText');//取得所有購物車數量的input列表
cartNumText.forEach((item)=>{//對每個input逐一綁定change事件
item.addEventListener('change',function(e){
//取得該購物車id和數量,傳進EditCartNum
const cartId = e.target.getAttribute('data-cart-id');
const cartVal = parseInt(e.target.value);
EditCartNum(cartId,cartVal);//此函式會patch API
})
})
}
```
4. 呼叫API,patch購物車數量
```javascript=
//修改購物車數量
function EditCartNum(cartId,cartVal){
axios.patch(`${baseUrl}/api/livejs/v1/customer/${api_path}/carts`,
{
"data": {
"id": cartId,
"quantity": cartVal
}
})
.then((response)=>{
getCartList();
sweetAlert('修改單筆購物車成功','success');
})
.catch((error)=>{
console.log(error.response.data);
})
}
```
:::
- 表單驗證
- 發現助教的範例第一行 選取inputs時會選不到最後一個select(雖然select其實不需要驗證)
```javascript=
//助教的
const inputs = document.querySelectorAll("input[name],select[data=payment]");
//修改後的
const inputs = document.querySelector(".orderInfo").querySelectorAll("input[name],select");
//->先選到表單,再選表單下所有input,如果是助教的方式可能會選到頁面中的其他input
```
### 法希娜#3206
->>完成:
一般商品顯示
購物車顯示+正確總額
購物車單筆刪除
全部刪除
加入購物車
送出訂單
->>待完成:輸入驗證/ 送出訂單後清空購物車
**本次新增知識點:
(一)抓取擁有同樣class但各自不同id的元素,求個別click事件的該id
```JavaScript
const addCardBtn = document.querySelectorAll('.addCardBtn');
addCardBtn.forEach((btn) =>{
btn.addEventListener('click', (e) => {
const idVal = e.target.getAttribute('id');
})
})
```