第七阶段-加入购物车
构建思路
加入购物车模块的功能实现说明
商品类与购物车类的构建与测试
由上述讨论可知,在这里将购物车信息保存在session
域中。这是因为每个购物车对应唯一一个用户,且在服务器中,用户信息也是保存在Session
中,因此有一个session
域对应唯一一个用户,也因此对应一个购物车。
进一步,根据上述的的数据库表,可以可知每一个购物车项中应当包含:商品名称、商品数量、商品单价、商品金额以及总价格。因此购物车类只需要包含上述五种成员变量即可,对应代码如下:
public class CartItem {
private Integer id;
private String name;
private Integer count;
private BigDecimal price;
private BigDecimal totalPrice;
public CartItem() {
}
public CartItem(Integer id, String name, Integer count, BigDecimal price, BigDecimal totalPrice) {
this.id = id;
this.name = name;
this.count = count;
this.price = price;
this.totalPrice = totalPrice;
}
// getset方法
}
因此购物车就是包含了各种CartItem
的集合类,同时提供一些可以获取一些统计性的据(商品总数+商品总价)方法。此外,由于在购物车中,可能会经常出现循环遍历比较判断操作,故这里考虑使用map
来作为存取数据的数据结构,其中key
是商品的id,value
是对应的商品项,并且用链接的HashMap
来实现,对应集合类代码如下,
private Map<Integer, CartItem> items = new LinkedHashMap<>();
有了集合类,就需要定义针对该集合类的操作方法
加入购物车
**需求:在主页点击了”加入购物车按钮“**添加某个商品项目后,除了请求servlet将数据保存至session外,还需要返回添加对应商品的页面,同时需要在主页中显示”成功添加xxx商品“以及当前商品的数量。
需求分析:当点击添加购物车后,应当携带着当前被点击商品项的信息(以id作为标识符)去访问CartServlet
程序,在Servlet
程序中,需要将该商品信息添加至Cart类的items中(如果当前存在Cart类,就直接调用添加,否则就新建)。同时需要在index.jsp中的session
域中再保存一个lastAddItem
用于回显最近一次被添加到商品,Cart类中对应代码如下:
/*
* 输入:一个带添加到商品项
*/
public void addItem(CartItem cartItem) {
// 先查看购物车中是否已经添加过此商品,如果已添加,则数量累加,总金额更新,
// 如果没有添加过,直接放到集合中即可
CartItem item = items.get(cartItem.getId());
if (item == null) {
// 之前没添加过此商品
items.put(cartItem.getId(), cartItem);
}else {
// 数量 累加
item.setCount(item.getCount() + 1);
// 更新总金额
item.setTotalPrice(item.getPrice().multiply(new BigDecimal(item.getCount())));
}
}
CartServlet
程序中加入购物车模块代码如下
protected void addItem(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.获取并处理用户输入的数据
String id = request.getParameter("id");
int id = WebUtils.parseInt(id, 0);
// 2.从数据库中查询该id对应的信息,同利用该信息构造一个新的cartItem项
Book book = bookService.queryById(id);
CartItem cartItem = new CartItem(book.getId(), book.getName(), 1, book.getPrice(), book.getPrice());
// 3.判断当前session中是否有cart,如果有则直接获取,否则新建并将其装入sessiong域中
Cart cart = (Cart) request.getSession().getAttribute("cart");
if(cart == null){
cart = new Cart;
request.getSession().setAttribute("cart", cart);
}
// 4.将step2中创建的item项装入购物车
cart.addItem(cartItem);
// 5.地址将最后一次添加的商品项放入session域
request.getSession().setAttribute("lastName",cartItem.getName());
// 6.响应重定向至原先商品所在的地址页面
String referer = request.getHeader("Referer") //得到之前的地址
response.sendRedireciton(referer);
}
⚠️ 回跳地址不能写死为主页,应当写成返回上一个地址
index.jsp中需要将最近一次添加入购物车的商品项回显至点击页面,同时给购物车按钮绑定单击事件
<div class="book_add">
<button bookId="${book.id}" class="addToCart">加入购物车</button>
</div>
因此也需要添加如下内容(这里用了jQuery对象):
<c:if test="${not empty sessionScope.cart}">
<%--购物车非空的输出--%>
<span id="cartTotalCount">您的购物车中有${sessionScope.cart.totalCount}件商品</span>
<div>
您刚刚将<span style="color: red" id="cartLastName">${sessionScope.lastName}</span>加入到了购物车中
</div>
</c:if>
<script type="text/javascript">
$(function () {
//给加入购物车按钮绑定单击时间
$("button.addToCart").click(function() {
/**
* 在事件响应的function函数 中,有一个this对象,这个this对象,是当前正在响应事件的dom对象
* @type {jQuery}
*/
var bookId = $(this).attr("bookId");
location.href = "http://localhost:8080/07_book/cartServlet?action=addItem&id=" + bookId;
})
})
</script>
效果图:
显示购物车
需求:单独一个页面用于显示已经加入购物车中的商品项,要包含商品名称、数量、单价、金额以及删除,清空等操作。且数量可以再购物车中进行修改。
因此单独定义一个cart.jsp页面,用于显示购物车中的商品,代码如下
如果当前数据为空--%>
<c:if test="${empty sessionScope.cart.items}">
<td colspan="5"><a href="index.jsp">空!!!!!继续购物!!</a></td>
</c:if>
<%-- 如果购物车非空—— 遍历输出 --%>
<c:if test="${not empty sessionScope.cart.items}">
<c:forEach items="${sessionScope.cart.items}" var="entry">
<tr>
<td>${entry.value.name}</td>
<td>
<input class="updateCount" bookId="${entry.value.id}" type="text" value="${entry.value.count}">
</td>
<td>${entry.value.price}</td>
<td>${entry.value.totalPrice}</td>
<td><a class="deleteItem" href="cartServlet?action=deleteItem&id=${entry.value.id}">删除</a></td>
</tr>
</c:forEach>
</c:if>
⚠️ 对于动态生成的不止一个的标签(例如第16行的a标签),如果使用id
选择器的话,由于id选择的唯一性,只有第一个id能被访问的。在这里就是只有第一个删除按钮有效。故对于动态生成的标签,应当构造class
属性,并用class
属性选择器来选择并绑定事件。但如果是唯一性的标签,则可以用id选择器进行绑定,例如后面提到的清空购物车。
效果图:
删除与清空购物车
需求:在cart.jsp页面删除购物车或者清空购物车时,会有页面提示询问是否删除对应的项目。
需求分析:在cart类中删除某一项对应着items.remove(id)
,清空则对应着items.clear()
。在servlet
程序中对于删除,只需要传递id信息即可,对于清空,什么都不需要传递。对应的cart类中的方法如下:
/**
* 删除商品项
*/
public void deleteItem(Integer id) {
items.remove(id);
}
/**
* 清空购物车
*/
public void clear() {
items.clear();
}
对应的servlet程序如下
/*
* 删除某个购物车项
*/
protected void deleteItem(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.获取商品编号
int id = WebUtils.parseInt(request.getParameter("id"), 0);
// 2.从session域中获取购物车对象
Cart cart = (Cart) request.getSession().getAttribute("cart");
if (cart != null) {
// 删除 对应id的 商品项
cart.deleteItem(id);
// 重定向回原来购物车展示页面
String referer = request.getHeader("Referer");//得到之前的地址
response.sendRedirect(referer);
}
}
/*
* 清空购物车
*/
protected void clear(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1 获取购物车对象
Cart cart = (Cart) request.getSession().getAttribute("cart");
if (cart != null) {
// 清空购物车
cart.clear();
// 重定向回原来购物车展示页面
String referer = request.getHeader("Referer");//得到之前的地址
response.sendRedirect(referer);
}
}
在jsp页面中也要绑定对应的单击事件
$(function () {
//给 删除 绑定单事件
$(".deleteItem").click(function () {
return confirm("确定删除" + $(this).parent().parent().find("td:first").text() + "吗?");
});
//给 清空购物车 绑定单击事件
$("#clearCart").click(function () {
return confirm("确定清空购物车?");
});
})
说明:这里的clearCart可以用id选择器,但deleteItem只能用class选择器。
修改购物车数据
修改购物车数据主要是在如下的输入框中进行
上述的输入框中需要在cart.jsp中增加一个用户数据框,默认值时当前记录在session中的商品数目
<td>
<input class="updateCount" bookId="${entry.value.id}" type="text" value="${entry.value.count}">
</td>
对该标签绑定change事件——一律转换为dom对象
//给 输入框 change内容改变事件(省去了blur的判断)
$(".updateCount").change(function () {
//获取商品名称
var name = $(this).parent().parent().find("td:first").text();
var id = $(this).attr("bookId");
//获取商品数量
var count = this.value;
if (confirm("确定修改购物车中 " + name + " 的数量为:" + count + "吗?") ) {
//发起请求。给服务器保存修改
location.href = "http://localhost:8080/07_book/cartServlet?action=updateCount&id=" + id + "&count=" + count;
}else {
// defaultValue属性是表单项Dom对象的属性。它表示默认的value属性值。
this.value = this.defaultValue;
}
});
其他技能
累加测试
在测试cart时,添加修改应做复制粘贴,对增-删-改依次作复制粘贴累加测试,这样能最大程度上避免后面测试出现打错造成测试失败的问题。
chrome中debug
在chrome中,可以进入source中查看源码并添加断点,通过输入console.log(12314)
,来测试是哪一部分的代码除了问题