day4
页面的设计与编写基本完成,接下来使用我们之前搭建好的服务器与相关的网络接口将鸿蒙中的逻辑真正实现一下。
在实现购物车页面展示功能时,使用了如下代码:
getCartList(uid: number): Promise<CartItem[]> {
return new Promise((resolve, reject) => {
axios.get(
`${this.baseUrl}/cart?uid=${uid}`
)
.then((resp) => {
resolve(resp.data)
})
.catch((error) => {
reject(error);
})
})
}
结果页面并未加载成功,日志显示错误:
随后通过postman测试服务端,显示返回结果为:
与CartItem类属性进行对比
并未发现差错。
进一步通过console.log发现,错误信息并非来自于axios请求,而是在调用model方法的.catch中被捕获的。这时服务器响应的数据已经正确被axios接收到了。
所以问题应当出现在调用的部分:
aboutToAppear() {
CartModel.getCartList(CommentConstant.user.id)
.then((list) => {
this.productList = list;
this.evalData(this.productList);
})
.catch((error) => {
console.log(error);
})
}
在一个偶然的巧合下,我将 this.evalData(this.productList);
注释掉,随后报错消失了。随后我用注释法(注释掉一部分代码看是否还报错)找到了 CartItem
类下的 getProduct()
方法。我使用 new CartItem().gerProduct()
加控制台输出的方式证明了这个方法 的清白 ,然而为什么通过服务器传过来的对象调用这个方法却会报 TypeError
的错呢?
最后的解决办法是从服务器接收数据的时候新创建一个对象,然后把接收对象的属性一条一条放到新对象中在使用。
推测可能的错误原因是,服务器传过来的这个对象仅仅“虚有其表”,仅仅包含了这个类的属性,却没有绑定这个类的方法,所以当尝试调用这个虚有其表的对象的方法时,编译器不会报错,但运行时抛报无法调用的异常。(也许与router传对象参数也无法使用的原因类似?)
最后页面成功显示了数据:
值得一提的是,删除一项时,我最初的想法是将购买数量设为 0,在数据库中添加触发器,当一项变为0后删除该数据:
DELIMITER $$
CREATE TRIGGER delete_from_cart_before_update
BEFORE UPDATE ON cart
FOR EACH ROW
BEGIN
IF NEW.count = 0 THEN
DELETE FROM cart WHERE uid = NEW.uid AND Did = NEW.Did;
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Row deleted due to count being zero.';
END IF;
END$$
DELIMITER ;
当我后来用postman测试时,数据库报了这样一个错:
我才意识到这是个愚蠢想法:在update触发器中使用delete是大忌,因为delete会重新触发触发器,导致无限循环或更严重的错误。所以还是应当老老实实写delete语句。
关于同步和异步的问题:在声明周期函数中使用异步调用服务器数据是比较常见的行为,当时如果期望取到了第一个数据再请求第二个数据时,有以下两种办法:
-
使用await与async将生命周期内部变为同步,但是依然需要注意的是,虽然使用async将aboutToAppear变为异步,但是程序不会等待aboutToAppear内部代码执行完毕再渲染页面,因为拿到aboutToAppear的Promise是一瞬间完成的。
async aboutToAppear() { await 请求1 .then().catch() await 请求2 .then().catch() }
-
在一个Promise的then中继续下一个请求,这样可以保证请求的执行是顺序的。缺点是代码较乱。
aboutToAppear() { 请求1 .then(() => { 请求2 .then().catch() }).catch() }
在查询订单时,需要实现的功能为根据用户编号uid查询所有订单以及订单中第一个商品的信息。使用了较为复杂的多表连结查询和分组查询,使用的sql语句如下:
select store.storeName,dish.sid,orders.state,time,dish.price,dish.dishName,img from orders
join store on orders.sid = store.id
join dish on store.id = dish.sid
join user on orders.uid = user.id
where user.id = 1 and orders.state = 0 and dish.id = (
select min(dish.id) from dish where dish.sid = store.id
) group by store.id;
实际执行时产生报错:
网上的解释为
在Mysql版本为5.7.25时,在使用使用group by 时,会出现Expression #4 of SELECT list is not in GROUP BY clause and contains nonaggregated...错误,这个错误的原因是 group by后面需要加上,select中的所有字段。不然就会报这个错误。
修改并规范后的sql代码
SELECT
store.storeName,
store.id AS sid, -- 明确sid来源并使用AS给列一个别名以避免混淆
orders.state,
orders.time, -- 假设time是orders表的列
dish.price,
dish.dishName,
dish.img AS img -- 假设img是store表的列,明确了来源
FROM
orders
JOIN
store ON orders.sid = store.id
JOIN
dish ON store.id = dish.sid AND dish.id = (
SELECT
MIN(dish.id)
FROM
dish
WHERE
dish.sid = store.id
)
JOIN
user ON orders.uid = user.id
WHERE
user.id = 1 AND orders.state = 0
GROUP BY
store.storeName, store.id, orders.state, orders.time, dish.price, dish.dishName, dish.img;
一个订单有可能包含多个商品,由axios将商品信息一个一个传递给服务端无疑是最低效的方法。而将所有数据一次性传递给服务器端,则需要使用post请求,将传递的商品信息放在TCP报文的数据段。这就要求了服务器需要能够处理 json 数据并具备一次性插入多条数据至数据库的功能。
该功能可以使用xml字符串拼接完成:
@Insert("<script> " +
"insert into od " +
"values " +
"<foreach collection=\"items\" index=\"index\" item=\"item\" separator=\",\"> " +
"(#{item.Oid},#{item.Did},#{item.count})" +
"</foreach> " +
"</script>")
public int insertOD(@Param("items") List<Od> items);