删除购物车餐品
一、前端处理
在前端,我们的主要步骤是,添加点击事件,实现调用的函数。大多数的功能都是这两步就可以实现。
在删除处添加点击事件:
html
<button onclick="delMulti()" type="button" class="btn btn-danger"
style="width:80px;">删除</button>
第二步,实现delMulti函数:
javascript
const delMulti = async () => {
const boxes = document.querySelectorAll(".my-box:checked");
if (boxes.length === 0) {
toast("错误!", "请选择要删除的商品!");
return;
}
const idArgs = [...boxes].map(box => `meal_id=${box.value}`).join("&");
const resp = await fetch(`cart/del?${idArgs}`);
if (!resp.ok) {
toast("错误!", "删除失败!");
return;
}
const result = await resp.json();
if (!result.success) {
toast("失败!", result.message);
return;
}
getList();
toast("成功!", "删除成功");
}
函数的解释:
-
const boxes = document.querySelectorAll(".my-box:checked");
:这行代码获取所有选中的 .my-box 类的复选框元素。其中.my-box:checked 用于选择具有 my-box 类且被选中的复选框(checkbox)元素。这里的 :checked 是一个伪类选择器,它匹配任何被选中的复选框或单选按钮(radio button)。
具体来说,这个选择器的组成部分如下:
.my-box
:选择所有具有 my-box 类名的元素。
:checked
:从上述选中的元素中,进一步选择那些被用户勾选的元素。 -
if (boxes.length === 0) { ... }
:如果没有选中任何复选框,会显示一个错误提示,并返回,不执行后续操作。也就是用户没勾选任何一个商品自然也就不能进行删除操作了。 -
const idArgs = [...boxes].map(box => meal_id=${box.value}).join("&");
:这行代码将选中的复选框的值(假设每个复选框的 value 属性代表商品的ID)转换成查询参数字符串,用于构建请求URL。没学过可能难以理解。在这里拆开来理解:
[...boxes]
:使用扩展运算符(spread operator)将 NodeList 对象转换为数组。这是因为 NodeList 对象不是真正的数组,而是一个类数组对象,不能直接使用数组的方法如 map。
.map(box => meal_id=${box.value})
:使用 map 方法遍历数组中的每个复选框元素。对于每个元素,它创建一个字符串,格式为 meal_id=<box的value值>。
.join("&")
:使用 join 方法将数组中的所有字符串连接成一个单一的字符串,每个字符串之间用 & 符号连接。这样,如果有三个选中的复选框,其 value 分别为 1、2 和 3,最终生成的字符串将是meal_id=1&meal_id=2&meal_id=3
。 -
const resp = await fetch(cart/del?${idArgs});
:使用 fetch API 发送一个请求到服务器的 cart/del 路径,并带上之前构建的查询参数,在这里就和之前的一样了。 -
if (!resp.ok) { ... }
:如果响应的状态码不是2xx(即请求失败),会显示一个错误提示,并返回。 -
const result = await resp.json();
:将响应体解析为JSON格式。 -
if (!result.success) { ... }
:如果响应的JSON中的 success 属性不是 true(即操作未成功),会显示一个错误提示,并返回。 -
getList();
:如果删除成功,调用 getList 函数,用于刷新或更新商品列表的函数。 -
toast("成功!", "删除成功");
:最后,如果一切顺利,会显示一个成功的提示。
到这里前端实现完成,开始实现后端逻辑
二、后端处理
这是关于购物车的操作,所以我们的功能实现要在CartServlet类中进行添加删除购物车餐品功能。专门的功能在专门的类中进行,代码结构,层次会比较清晰。
老配方,三步法:加注解路径,加case板块,实现核心逻辑功能函数。
- 加注解路径:
java
@WebServlet({
"/cart/list",
"/cart/num",
"/cart/del"
})
- 加case板块:
java
case "/cart/del":
cartDel(req, resp);
break;
- 实现核心逻辑功能函数:
java
private void cartDel(HttpServletRequest req, HttpServletResponse resp) throws IOException {
User user = (User) req.getSession().getAttribute("CurrUser");
if (user == null) {
MyWeb.printJson(resp, R.err("请先登录"));
return;
}
String[] mealIds = req.getParameterValues("meal_id");
if (mealIds == null || mealIds.length == 0) {
MyWeb.printJson(resp, R.err("请选择商品!"));
return;
}
String condition = Arrays.stream(mealIds).map(id -> "meal_id=" + id).collect(Collectors.joining(" or "));
DaoCreater.currentDao().update("delete from t_shoppingcart where u_id = ? and (" + condition + ")", user.getU_id());
MyWeb.printJson(resp, R.OK());
}
简单进行一下函数解释:
-
User user = (User) req.getSession().getAttribute("CurrUser");
:从当前会话中获取当前用户对象。 -
if (user == null) { ... }
:如果当前会话中没有用户对象(即用户未登录),则返回一个错误响应,提示用户需要先登录。 -
String[] mealIds = req.getParameterValues("meal_id");
:从请求中获取名为 meal_id 的参数值数组。这个参数应该是我们前端发送过来的也就是URL后的参数,包含要删除的商品ID。 -
if (mealIds == null || mealIds.length == 0) { ... }
:如果没有提供 meal_id 参数,或者参数数组为空,则返回一个错误响应,提示用户需要选择商品。 -
String condition = Arrays.stream(mealIds).map(id -> "meal_id=" + id).collect(Collectors.joining(" or "));
:这个是使用Java 流(Stream)和lambda表达式构建一个SQL条件字符串。对于每个 meal_id,它创建一个条件子句 "meal_id=" + id,然后将所有子句用 " or " 连接起来,形成一个完整的SQL WHERE 子句条件。6.
.map(id -> "meal_id=" + id)
:对流中的每个元素(在这个例子中是 mealIds 数组中的每个ID)应用一个映射函数。这个映射函数将每个ID转换为一个字符串,格式为 "meal_id=" + id。这是构建SQL查询条件的一部分。
.collect(Collectors.joining(" or "))
:收集流中的所有元素,并将它们连接成一个单一的字符串。Collectors.joining 是一个收集器,它将流中的字符串元素连接起来,并在每个元素之间插入指定的分隔符。在这个例子中,分隔符是 " or ",这意味着如果流中有多个元素,它们将被连接成一个由 " or " 分隔的字符串。例如,如果 mealIds 数组包含 ["1", "2", "3"],那么这行代码将生成字符串 "meal_id=1 or meal_id=2 or meal_id=3"。
-
DaoCreater.currentDao().update("delete from t_shoppingcart where u_id = ? and (" + condition + ")", user.getU_id());
:调用数据访问对象(DAO)来执行一个SQL更新(删除)操作。这个操作会从 t_shoppingcart 表中删除所有属于当前用户(u_id)且符合之前构建的条件的商品。
8. MyWeb.printJson(resp, R.OK());
:最后,如果删除操作成功,返回一个JSON格式的成功响应。
如果函数理解的话,那么代码实现起来还是容易。
注意:在这里的直接将用户输入拼接到SQL查询中的做法存在SQL注入的风险,一般还是用参数化的查询较好。
到这里前后端的代码都已经完成了,唯一要做的就是根据报错的地方进行排查了,毕竟没有人能保证代码不出错,学会排查bug也是能力的体现。