目录
1. Cookie了解
创建一个新的module webmodule16-cookie-kaptcha-js ,添加web支持框架。同时导入tomcat包。
HTTP协议本身是无状态的。单靠HTTP协议本身无法判断一个请求来自于哪一个浏览器,所以也就没法识别用户的身份状态。
①Cookie本质
在浏览器端临时存储数据
键值对
键和值都是字符串类型
数据量很小
②Cookie在浏览器和服务器之间的传递
没有Cookie的状态
在服务器端没有创建Cookie并返回的情况下,浏览器端不会保存Cookie信息。双方在请求和响应的过程中也不会携带Cookie的数据。
1. 创建Cookie对象 在客户端保存Cookie
@WebServlet("/cookie01")
public class CookieServlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 创建Cookie对象
Cookie cookie = new Cookie("uname", "jim");
// 2. 将Cookie对象添加到响应中
resp.addCookie(cookie);
req.getRequestDispatcher("hello01.html").forward(req, resp);
}
}
2. 设置Cookie的有效时长
设置cookie的有效时长是60秒
cookie.setMaxAge(60)
上网时间长了,本地会保存很多Cookie。对浏览器来说,访问互联网资源时不能每次都把所有Cookie带上。浏览器会使用Cookie的domain和path属性值来和当前访问的地址进行比较,从而决定是否携带这个Cookie
cookie.setDomain(pattern)
cookie.setPath(uri)
3. Cookie的应用
1. 记住用户名和密码十天 setMaxAge(60 * 60 * 24 * 10)
2. 十天免登录
2. kaptcha
1. kaptcha如何使用
添加jar
在web.xml文件中注册KaptchaServlet,并设置验证码图片的相关属性,kaptcha验证码图片的各个属性在常量接口:Constants中。这个可以从jar包的Constants里面看到字段名称,然后相应设置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>KaptchaServlet</servlet-name>
<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
<init-param>
<param-name>kaptcha.border.color</param-name>
<param-value>red</param-value>
</init-param>
<init-param>
<param-name>kaptcha.textproducer.char.string</param-name>
<param-value>abcdefg</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>KaptchaServlet</servlet-name>
<url-pattern>/kaptcha.jpg</url-pattern>
</servlet-mapping>
</web-app>
在html页面上编写一个img标签,然后设置src等于KaptchaServlet对应的url-pattern。此时我们访问静态页面的html,即可显示动态变化的验证码图片。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<img src="kaptcha.jpg" alt="">
</body>
</html>
2. KaptchaServlet在生成验证码图片时,会同时将验证码信息保存到session中,key在Constants也有,是KAPTCHA_SESSION_KEY
因此,我们在注册请求时,首先将用户文本框中输入的验证码值和session中保存的值进行比较,相等,则进行注册,我们每次访问它其实它会动态变化,并覆盖放入session中,我们可以写一个Servlet去检验一下
@WebServlet("/kaptcha01")
public class KaptchaServletDemo01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
Object obj = session.getAttribute("KAPTCHA_SESSION_KEY");
System.out.println("obj = " + obj);
}
}
3. 注册功能实装
3.1 注册的初步实现
现在我们创立新的模块,然后导入kaptcha包,同时把这个最外层的用户验证过滤器注释掉,方便我们先把注册功能实装。
因为我们的登录页面放在user文件下的regist.html,所以tomcat的默认访问主页改为
http://localhost:8080/webmodule16/page.do?operate=page&page=user/regist
然后改写静态html,加入Thymeleaf。首先把路径都改为thymeleaf的,然后把表单的action,改为和之前login页面相同的user.do,然后method因为是表单,应该写为post,而因为是post没法直接带过去operate,所以写一个input的隐藏域,name=operate,而value=regist。(可以直接复制之前的login.html下的这部分),而kaptchaServlet的配置,放在web.xml下,我们就能在前端配置验证码图片的地方写入url-pattern
<servlet>
<servlet-name>KaptchaServlet</servlet-name>
<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
<init-param>
<param-name>kaptcha.image.width</param-name>
<param-value>120</param-value>
</init-param>
<init-param>
<param-name>kaptcha.image.height</param-name>
<param-value>50</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>KaptchaServlet</servlet-name>
<url-pattern>/kaptcha.jpg</url-pattern>
</servlet-mapping>
<div>
<label>验证码:</label>
<div class="verify">
<input type="text" placeholder=""/>
<img th:src="@{/kaptcha.jpg}" alt=""/>
</div>
</div>
为了真的完成注册功能,我们就需要给UserDAO和UserService层写添加用户的方法了,这里先不考虑用户名,邮箱重复的问题。
public interface UserDAO {
public User getUser(String uname, String pwd);
public void addUser(User user);
}
-------------------------------------------------------------------------------------
public class UserDAOImpl extends BaseDao implements UserDAO {
@Override
public User getUser(String uname, String pwd) {---}
@Override
public void addUser(User user) {
String sql = "INSERT INTO t_user VALUES (0, ?, ?, ?, 0);";
try {
executeUpdate(sql, user.getUname(), user.getPwd(), user.getEmail());
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("UserDAO.addUser出错了");
}
}
}
-------------------------------------------------------------------------------------
public interface UserService {
public User login(String uname, String pwd);
public void regist(User user);
}
-------------------------------------------------------------------------------------
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
@Override
public User login(String uname, String pwd) {---}
@Override
public void regist(User user) {
userDAO.addUser(user);
}
}
这里UserController里写regist方法,形参应该是从注册页面获取,当然为了完成验证码比较,需要写HttpSession,除了账号密码邮箱外,还应该获取验证码的Code,这里我们别忘了把输入验证码的input地方写上相应参数value=verifyCode,也别忘了给用户名密码邮箱的input表单写value值。
<label>验证码:</label>
<div class="verify">
<input type="text" name="verifyCode" placeholder=""/>
<img th:src="@{/kaptcha.jpg}" alt=""/>
</div>
这里老师其实为了让输错验证码不出现直接跳转没有提示,使用response回应一个流写了js代码,让它提示密码错误。主要因为myssm这里已经封装jar包,本来可以return null,然后改写DispatcherServlet那里,如果为null就return,或者是返回空字符串,总之为了实现我们写的js方法跳转。因为已经封装jar包了,这里就不这么改了。
public class UserController {
private UserService userService = null;
private CartItemService cartItemService = null;
public String login(String uname, String pwd, HttpSession session) {
User user = userService.login(uname, pwd);
if (user != null) {
Cart cart = cartItemService.getCart(user);
user.setCart(cart);
session.setAttribute("currUser", user);
return "redirect:book.do";
}
return "user/login";
}
public String regist(String uname, String pwd, String email, String verifyCode,
HttpSession session, HttpServletResponse resp) throws IOException {
Object kaptchaCodeObj = session.getAttribute("KAPTCHA_SESSION_KEY");
if (kaptchaCodeObj == null || !kaptchaCodeObj.equals(verifyCode)){
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
//out.println("<script language='javascript'>alert('验证码不正确!');window.location.href='page.do?operate=page&page=user/regist';</script>");
out.println("<script language='javascript'>alert('验证码不正确!');</script>");
//return "user/regist";
return "user/regist";
} else{
if (verifyCode.equals(kaptchaCodeObj)){
userService.regist(new User(uname, pwd, email));
return "user/login";
}
}
return "user/login";
}
}
3.2 正则表达式了解
Road 2 Codinghttps://www.r2coding.com/#/README?id=%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F1)正则表达式的使用三步骤:
1. 定义正则表达式对象
正则表达式定义有两个方式:
1) 对象形式
var reg = new RegExp("abc")
2) 直接量形式
var reg = /abc/;
test方法是只要存在一个满足要求的,就返回true
<script language="JavaScript">
var reg1 = new RegExp("abc");
var reg2 = /abd/;
var str = "abcdefg";
var flag1 = reg1.test(str);
var flag2 = reg2.test(str);
console.log(flag1);
console.log(flag2);
</script>
3) 匹配模式:
- g 全局匹配 global
<script language="JavaScript">
var reg1 = /o/g;
var reg2 = /o/;
var str = "abcoabco";
str1 = str.replace(reg1, "_");
str2 = str.replace(reg2, "_");
console.log(str1);
console.log(str2);
</script>
- i 忽略大小写匹配 ignore
- m 多行匹配
- gim这三个可以组合使用,不区分先后顺序
例如: var reg = /abc/gim , var reg = new RegExp("abc","gim");
2. 定义待校验的字符串
3. 校验
2)元字符 和 [ ]表示集合
<script language="JavaScript">
var reg = /\w/gim;
var str = "java%s\nc@ript";
str = str.replace(reg, "A");
console.log(str);
</script>
3) 出现的次数
<script language="JavaScript">
var reg = /[a]{2}/gim;
var str = "aaaaaaaaaaaajava%s\nc@ript";
str = str.replace(reg, "#");
document.write(str);
</script>
3.3 注册页面的表单验证
原本的注册页面html
<form th:action="@{/user.do}" method="post">
<input type="hidden" name="operate" value="regist">
<div class="form-item">
<div>
<label>用户名称:</label>
<input type="text" name="uname" value="王志伟" placeholder="请输入用户名"/>
</div>
<span class="errMess">用户名应为6~16位数组和字母组成</span>
</div>
<div class="form-item">
<div>
<label>用户密码:</label>
<input type="password" name="pwd" value="ok" placeholder="请输入密码"/>
</div>
<span class="errMess">密码的长度至少为8位</span>
</div>
<div class="form-item">
<div>
<label>确认密码:</label>
<input type="password" name="pwd" value="ok" placeholder="请输入确认密码"/>
</div>
<span class="errMess">密码两次输入不一致</span>
</div>
<div class="form-item">
<div>
<label>用户邮箱:</label>
<input type="text" name="email" value="wang@125.com" placeholder="请输入邮箱"/>
</div>
<span class="errMess">请输入正确的邮箱格式</span>
</div>
<div class="form-item">
<div>
<label>验证码:</label>
<div class="verify">
<input type="text" name="verifyCode" placeholder=""/>
<img th:src="@{/kaptcha.jpg}" alt=""/>
</div>
</div>
<span class="errMess">请输入正确的验证码</span>
</div>
<button class="btn">注册</button>
</form>
<form>有一个事件 onsubmit
οnsubmit="return false" , 那么表单点击提交按钮时不会提交
οnsubmit="return true" , 那么表单点击提交按钮时会提交
我们希望表单发送之前能对表单的数据进行校验,如果不满足会显示我们的提示span,如用户名不能为空等。其次要校验满足条件才能提交表单,这里我们就写一个preRegist()的js方法,来完成表单验证。
<script language="JavaScript" th:src="@{/static/script/regist.js}"></script>
获取文档中某一个节点的方式
DOM:Document
var unameTxt = document.getElementById("unameTxt");
BOM:Browser
document.forms[0].uname
我们给输入的表单的id都叫对应的name值 + Txt 的字符名称,而输入表单如果出错显示的span的id设置为对应name值 + Span 的字符名称。
function $(id){
return document.getElementById(id);
}
function preRigist(){
var unameTxt = $("unameTxt");
var unameReg = /^[a-zA-Z\d]{6, 16}$/;
var unameSpan = $("unameSpan");
if (!unameReg.test(unameTxt)){
unameSpan.style.visibility = "visible";
return false;
}else{
unameSpan.style.visibility = "hidden";
}
return true;
}
<div class="form-item">
<div>
<label>用户名称:</label>
<input type="text" id="unameTxt" name="uname" value="王志伟" placeholder="请输入用户名"/>
</div>
<span class="errMess" id="unameSpan" >用户名应为6~16位数组和字母组成</span>
</div>
3.4 判断用户名是否已经被注册(原生Ajax方式)
1. Ajax
Asynchronous Javascript And XML(异步JavaScript和XML)
第一步: 客户端发送异步请求;并绑定对结果处理的回调函数
1) <input type="text" name="uname" οnblur="ckUname()"/> onblur:失去焦点的事件
<input type="text" id="unameTxt" name="uname" value="王志伟" onblur="ckUname(this.value)" placeholder="请输入用户名"/>
2) 定义ckUname方法
function ckUname(uname) {
createXMLHttpRequest();
var url = "user.do?operate=ckUname&uname=" + uname;
xmlHttpRequest.open("GET", url, true);
//设置回调函数
xmlHttpRequest.onreadystatechange = ckUnameCB;
//发送请求
xmlHttpRequest.send();
}
创建XMLHttpRequest对象
//如果想要发送异步请求,我们需要一个关键的对象 XMLHttpRequest
var xmlHttpRequest ;
function createXMLHttpRequest(){
if(window.XMLHttpRequest){
//符合DOM2标准的浏览器 ,xmlHttpRequest的创建方式
xmlHttpRequest = new XMLHttpRequest() ;
}else if(window.ActiveXObject){//IE浏览器
try{
xmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}catch (e) {
xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP")
}
}
}
XMLHttpRequest对象操作步骤:
open(url,"GET",true)
onreadyStateChange 设置回调
send() 发送请求
在回调函数中需要判断XMLHttpRequest对象的状态:
readyState(0-4) , status(200)
下面是完整的代码,事实上这里应该是只写了if判断条件部分,后续写完服务器端给客户端发送数据再补充相应的部分。
function ckUnameCB() {
if (xmlHttpRequest.readyState == 4 && xmlHttpRequest.status == 200) {
//xmlHttpRequest.responseText 表示 服务器端响应给我的文本内容
//alert(xmlHttpRequest.responseText);
var responseText = xmlHttpRequest.responseText;
// {'uname':'1'}
//alert(responseText);
if (responseText == "{'uname':'1'}") {
alert("用户名已经被注册!");
} else {
alert("用户名可以注册!");
}
}
}
第二步:服务器端做校验,然后将校验结果响应给客户端
需要我们给服务器端的userController写相应的ckUname方法,我们要做的是用户名验证,我们之前的登录验证需要从DAO层封装查询账号密码相同的User,这次的用户名验证需要看的是用户名是否重复,所以先去给DAO以及Service层写相应的方法。
public interface UserDAO {
public User getUser(String uname, String pwd);
public void addUser(User user);
public User getUser(String uname);
}
-------------------------------------------------------------------------------------
public class UserDAOImpl extends BaseDao implements UserDAO {
@Override
public User getUser(String uname, String pwd) {---}
@Override
public void addUser(User user) {---}
@Override
public User getUser(String uname) {
String sql = "SELECT * FROM t_user WHERE uname = ?;";
try {
List<User> users = executeQuery(User.class, sql, uname);
if (users.size() > 0) {
return users.get(0);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("UserDAO.getUser出错了");
}
return null;
}
}
-------------------------------------------------------------------------------------
public interface UserService {
public User login(String uname, String pwd);
public void regist(User user);
public void getUser(String uname);
}
-------------------------------------------------------------------------------------
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
@Override
public User login(String uname, String pwd) {---}
@Override
public void regist(User user) {---}
@Override
public void getUser(String uname) {
userDAO.getUser(uname);
}
}
然后写UserController,这里其实需要改DispatcherServlet,因为我们之前只做了redirect前缀的判断处理,这里涉及到Ajax需要给它特别处理
public class UserController {
private UserService userService = null;
private CartItemService cartItemService = null;
public String login(String uname, String pwd, HttpSession session) {---}
public String regist(String uname, String pwd, String email, String verifyCode,
HttpSession session, HttpServletResponse resp) throws IOException {---}
String ckUname(String uname){
User user = userService.getUser(uname);
if (user==null){
//用户名已经被占用,不可以注册
//这里最方便的是改DispatcherServlet,把带有ajax开头的地方专门处理
//return "ajax:1";
return "json:{'uname':'1}";
}else{
//用户名可以注册
return "json:{'uname':'0}";
//return "ajax:0";
}
}
}
这里我们直接把myssm1.0的jar包去除,然后复制上一个项目的myssm部分,然后改写DispatcherServlet的代码。这里else if的部分是新加入的。
// 3. 视图处理
String methodReturnStr = (String) returnObj;
if (methodReturnStr.startsWith("redirect:")) { //比如 "redirect:fruit.do"
String redirectStr = methodReturnStr.substring("redirect:".length());
resp.sendRedirect(redirectStr);
// 发送json数据
}else if(methodReturnStr.startsWith("json:")){
String jsonStr = methodReturnStr.substring("json:".length());
PrintWriter out = resp.getWriter();
out.println(jsonStr);
out.flush();
}else {
super.processTemplate(methodReturnStr, req, resp); //比如 "edit" "index"
}
}
既然给客户端发送了数据,就需要在客户端接受,这里接着去写之前的ajax的js代码
function ckUnameCB() {
if (xmlHttpRequest.readyState == 4 && xmlHttpRequest.status == 200) {
//xmlHttpRequest.responseText 表示 服务器端响应给我的文本内容
//alert(xmlHttpRequest.responseText);
var responseText = xmlHttpRequest.responseText;
// {'uname':'1'}
//alert(responseText);
if (responseText == "{'uname':'1'}") {
alert("用户名已经被注册!");
} else {
alert("用户名可以注册!");
}
}
}
然后运行,设置断点看看有没有出错
在Controller这里也设置断点
这里发现已经完成功能,彻底实现了对用户名的校验和异步的ajax,至此完成用户名校验功能。
Ajax : 异步的JavaScript and XML
目的: 用来发送异步的请求,然后当服务器给我响应的时候再进行回调操作
好处: 提高用户体验;局部刷新:降低服务器负担、减轻浏览器压力、减轻网络带宽压力
开发步骤:
1) 创建XMLHttpRequest
2) 调用open进行设置:"GET" , URL , true
3) 绑定状态改变时执行的回调函数 - onreadystatechange
4) 发送请求 - send()
5) 编写回调函数,在回调函数中,我们只对XMLHttpRequest的readystate为4的时候感兴趣
我们只对XMLHttpRequest的status为200的时候感兴趣
0: (Uninitialized) the send( ) method has not yet been invoked.
1: (Loading) the send( ) method has been invoked, request in progress.
2: (Loaded) the send( ) method has completed, entire response received.
3: (Interactive) the response is being parsed.
4: (Completed) the response has been parsed, is ready for harvesting.
0 - (未初始化)还没有调用send()方法
1 - (载入)已调用send()方法,正在发送请求
2 - (载入完成)send()方法执行完成,已经接收到全部响应内容
3 - (交互)正在解析响应内容
4 - (完成)响应内容解析完成,可以在客户端调用了
3. VUE和Axios
3.1 Vue入门
这里新建了项目,然后创建了一个新的html文件,然后把vue和Axios的js文件放入
指明vue路径
<script language="JavaScript" src="script/vue.js"></script>
0. 定义对象的两种方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script language="JavaScript" src="script/vue.js"></script>
<script language="JavaScript">
function hello(){
person.sayHello();
}
//定义js对象方式一:
/*
var person = new Object();
person.pid = "p001";
person.pname="jim";
person.sayHello = function(){
alert("HELLO world");
}
*/
//定义js对象方式二:
var person = {
"pid":"p001",
"pname":"jim",
"sayHello":function(){
alert("HELLO world");
}
};
window.onload=function(){
var vue = new Vue({
});
}
</script>
</head>
<body>
<div id="div0">
<span>HELLO</span>
<input type="button" value="打招呼" onclick="hello()"/>
</div>
</body>
</html>
1 {{}} - 相当于innerText
2 v-bind:attr 绑定属性值
例如,v-bind:value - 绑定value值
简写: :value
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script language="JavaScript" src="script/vue.js"></script>
<script language="JavaScript">
window.onload=function(){
var vue = new Vue({
"el":"#div0",
data:{
msg:"hello!!!",
uname:"鸠摩智"
}
});
}
</script>
</head>
<body>
<div id="div0">
<span>{{msg}}</span>
<!-- v-bind:value表示绑定value属性 , v-bind可以省略,也就是 :value -->
<!--<input type="text" v-bind:value="uname"/>-->
<input type="text" :value="uname"/>
</div>
</body>
</html>
3 v-model 双向绑定
v-model:value , 简写 v-model
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script language="JavaScript" src="script/vue.js"></script>
<script language="JavaScript">
window.onload=function(){
var vue = new Vue({
"el":"#div0",
data:{
msg:"hello!!!"
}
});
}
</script>
</head>
<body>
<div id="div0">
<span>{{msg}}</span><br/>
<!--
v-model指的是双向绑定,
也就是说之前的v-bind是通过msg这个变量的值来控制input输入框
现在 v-model 不仅msg来控制input输入框,反过来,input输入框的内容也会改变msg的值
-->
<!--<input type="text" v-model:value="msg"/>-->
<!-- v-model:value 中 :value可以省略,直接写成v-model -->
<!-- trim可以去除首尾空格 -->
<input type="text" v-model.trim="msg"/>
</div>
</body>
</html>
4 v-if , v-else , v-show
v-if和v-else之间不能有其他的节点
v-show是通过样式表display来控制节点是否显示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script language="JavaScript" src="script/vue.js"></script>
<script language="JavaScript">
window.onload=function(){
var vue = new Vue({
"el":"#div0",
data:{
num:2
}
});
}
</script>
</head>
<body>
<div id="div0">
<input type="text" v-model="num"/>
<!-- v-if和v-else之间不可以插入其他节点 -->
<!--<div v-if="num%2==0" style="width:200px;height:200px;background-color: chartreuse;"> </div>-->
<!--<br/>-->
<!--<div v-else="num%2==0" style="width:200px;height:200px;background-color: coral"> </div>-->
<!-- v-show -->
<!-- v-show是通过display属性来控制这个div是否显示 -->
<div v-show="num%2==0" style="width:200px;height:200px;background-color:blueviolet;"> </div>
</div>
</body>
</html>
5 v-for 迭代
v-for={fruit in fruitList}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script language="JavaScript" src="script/vue.js"></script>
<script language="JavaScript">
window.onload=function(){
var vue = new Vue({
"el":"#div0",
data:{
fruitList:[
{fname:"苹果",price:5,fcount:100,remark:"苹果很好吃"},
{fname:"菠萝",price:3,fcount:120,remark:"菠萝很好吃"},
{fname:"香蕉",price:4,fcount:50,remark:"香蕉很好吃"},
{fname:"西瓜",price:6,fcount:100,remark:"西瓜很好吃"}
]
}
});
}
</script>
</head>
<body>
<div id="div0">
<table border="1" width="400" cellpadding="4" cellspacing="0">
<tr>
<th>名称</th>
<th>单价</th>
<th>库存</th>
<th>备注</th>
</tr>
<!-- v-for表示迭代 -->
<tr align="center" v-for="fruit in fruitList">
<td>{{fruit.fname}}</td>
<td>{{fruit.price}}</td>
<td>{{fruit.fcount}}</td>
<td>{{fruit.remark}}</td>
</tr>
</table>
</div>
</body>
</html>
6 v-on 绑定事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script language="JavaScript" src="script/vue.js"></script>
<script language="JavaScript">
window.onload=function(){
var vue = new Vue({
"el":"#div0",
data:{
msg:"hello world!"
},
methods:{
myReverse:function(){
this.msg = this.msg.split("").reverse().join("");
}
}
});
}
</script>
</head>
<body>
<div id="div0">
<span>{{msg}}</span><br/>
<!-- v-on:click 表示绑定点击事件 -->
<!-- v-on可以省略,变成 @click -->
<!--<input type="button" value="反转" v-on:click="myReverse"/>-->
<input type="button" value="反转" @click="myReverse"/>
</div>
</body>
</html>
7 其他
trim:去除首尾空格 split() join()
watch表示侦听属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script language="JavaScript" src="script/vue.js"></script>
<script language="JavaScript">
window.onload=function(){
var vue = new Vue({
"el":"#div0",
data:{
num1:1,
num2:2,
num3:0
},
watch:{
//侦听属性num1和num2
//当num1的值有改动时,那么需要调用后面定义的方法 , newValue指的是num1的新值
num1:function(newValue){
this.num3 = parseInt(newValue) + parseInt(this.num2);
},
num2:function(newValue){
this.num3 = parseInt(this.num1) + parseInt(newValue) ;
}
}
});
}
</script>
</head>
<body>
<div id="div0">
<input type="text" v-model="num1" size="2"/>
+
<input type="text" v-model="num2" size="2"/>
=
<span>{{num3}}</span>
</div>
</body>
</html>
8 生命周期
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script language="JavaScript" src="script/vue.js"></script>
<script language="JavaScript">
window.onload=function(){
var vue = new Vue({
"el":"#div0",
data:{
msg:1
},
methods:{
changeMsg:function(){
this.msg = this.msg + 1 ;
}
},
/*vue对象创建之前*/
beforeCreate:function(){
console.log("beforeCreate:vue对象创建之前---------------");
console.log("msg:"+this.msg);
},
/*vue对象创建之后*/
created:function(){
console.log("created:vue对象创建之后---------------");
console.log("msg:"+this.msg);
},
/*数据装载之前*/
beforeMount:function(){
console.log("beforeMount:数据装载之前---------------");
console.log("span:"+document.getElementById("span").innerText);
},
/*数据装载之后*/
mounted:function(){
console.log("mounted:数据装载之后---------------");
console.log("span:"+document.getElementById("span").innerText);
},
beforeUpdate:function(){
console.log("beforeUpdate:数据更新之前---------------");
console.log("msg:"+this.msg);
console.log("span:"+document.getElementById("span").innerText);
},
updated:function(){
console.log("Updated:数据更新之后---------------");
console.log("msg:"+this.msg);
console.log("span:"+document.getElementById("span").innerText);
}
});
}
</script>
</head>
<body>
<div id="div0">
<span id="span">{{msg}}</span><br/>
<input type="button" value="改变msg的值" @click="changeMsg"/>
</div>
</body>
</html>
3.2 Axios入门
Axios是Ajax的一个框架,简化Ajax操作
Axios执行Ajax操作的步骤:
1. 添加并引入axios的js文件 2-1. 客户端向服务器端异步发送普通参数值
基本格式: axios().then().catch()
示例:
axios({
method:"POST",
url:"....",
params:{
uname:"lina",
pwd:"ok"
}
})
.then(function(value){})
//成功响应时执行的回调 value.data可以获取到服务器响应内容
.catch(function(reason){});
//有异常时执行的回调 reason.response.data可以获取到响应的内容
// reason.message / reason.stack 可以查看错误的信息
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>01.演示Axios发送普通的参数值给服务器端</title>
<script language="JavaScript" src="script/vue.js"></script>
<script language="JavaScript" src="script/axios.min.js"></script>
<script language="JavaScript">
window.onload=function(){
var vue = new Vue({
"el":"#div0",
data:{
uname:"lina",
pwd:"ok"
},
methods:{
axios01:function(){
axios({
method:"POST",
url:"axios01.do",
params:{
uname:vue.uname,
pwd:vue.pwd
}
})
.then(function (value) {
console.log(value);
})
.catch(function (reason) {
console.log(reason);
});
}
}
});
}
</script>
</head>
<body>
<div id="div0">
uname:<input type="text" v-model="uname"/><br/>
pwd:<input type="text" v-model="pwd"/><br/>
<input type="button" @click="axios01" value="发送一个带普通请求参数值的异步请求"/>
</div>
</body>
</html>
@WebServlet("/axios01.do")
public class Axios01Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String uname = request.getParameter("uname");
String pwd = request.getParameter("pwd");
System.out.println("uname = " + uname);
System.out.println("pwd = " + pwd);
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(uname+"_"+pwd);
throw new NullPointerException("这里故意抛出一个空指针异常....");
}
}
2-2. 客户端向服务器发送JSON格式的数据
什么是JSON
JSON是一种数据格式
XML也是一种数据格式
XML格式表示两个学员信息的代码如下
<students>
<student sid="s001">
<sname>jim</sname>
<age>18</age>
</student>
<student sid="s002">
<sname>tom</sname>
<age>19</age>
</student>
</students>
JSON格式表示两个学员信息的代码如下:
[{sid:"s001",age:18},{sid:"s002",age:19}]
JSON表达数据更简洁,更能够节约网络带宽
客户端发送JSON格式的数据给服务器端
1) 客户端中params需要修改成: data:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>02.演示Axios发送JSON格式的参数值给服务器端</title>
<script language="JavaScript" src="script/vue.js"></script>
<script language="JavaScript" src="script/axios.min.js"></script>
<script language="JavaScript">
window.onload=function(){
var vue = new Vue({
"el":"#div0",
data:{
uname:"lina",
pwd:"ok"
},
methods:{
axios02:function(){
axios({
method:"POST",
url:"axios02.do",
data:{
uname:vue.uname,
pwd:vue.pwd
}
})
.then(function (value) {
console.log(value);
})
.catch(function (reason) {
console.log(reason);
});
}
}
});
}
</script>
</head>
<body>
<div id="div0">
uname:<input type="text" v-model="uname"/><br/>
pwd:<input type="text" v-model="pwd"/><br/>
<input type="button" @click="axios02" value="发送JSON格式的参数值的异步请求"/>
</div>
</body>
</html>
2) 服务器获取参数值不再是 request.getParameter()...
而是:
StringBuffer stringBuffer = new StringBuffer("");
BufferedReader bufferedReader = request.getReader();
String str = null ;
while((str=bufferedReader.readLine())!=null){
stringBuffer.append(str);
}
str = stringBuffer.toString() ;
3) 我们会发现 str的内容如下:
{"uname":"lina","pwd":"ok"}
服务器端给客户端响应JSON格式的字符串,然后客户端需要将字符串转化成js Object
这里需要用第三方jar包Gson
Gson有两个API,可以通过 new Gson()生成Gson对象,然后调用它们
1.fromJson(string,T) 将字符串转化成java object
2.toJson(java Object) 将java object转化成json字符串,这样才能响应给客户端
将前端发送的json转化为java Object
Pojo对象
public class User {
private String uname ;
private String pwd ;
public User(){}
public User(String uname, String pwd) {
this.uname = uname;
this.pwd = pwd;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"uname='" + uname + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
axiosServlet
@WebServlet("/axios02.do")
public class Axios02Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
StringBuffer stringBuffer = new StringBuffer("");
BufferedReader bufferedReader = request.getReader();
String str = null ;
while((str=bufferedReader.readLine())!=null){
stringBuffer.append(str);
}
str = stringBuffer.toString() ;
//已知 String
//需要转化成 Java Object
Gson gson = new Gson();
//Gson有两个API
//1.fromJson(string,T) 将字符串转化成java object
//2.toJson(java Object) 将java object转化成json字符串,这样才能响应给客户端
User user = gson.fromJson(str, User.class);
System.out.println(user);
}
}
前端页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>02.演示Axios发送JSON格式的参数值给服务器端</title>
<script language="JavaScript" src="script/vue.js"></script>
<script language="JavaScript" src="script/axios.min.js"></script>
<script language="JavaScript">
window.onload=function(){
var vue = new Vue({
"el":"#div0",
data:{
uname:"lina",
pwd:"ok"
},
methods:{
axios02:function(){
axios({
method:"POST",
url:"axios02.do",
data:{
uname:vue.uname,
pwd:vue.pwd
}
})
.then(function (value) {
console.log(value);
})
.catch(function (reason) {
console.log(reason);
});
}
}
});
}
</script>
</head>
<body>
<div id="div0">
uname:<input type="text" v-model="uname"/><br/>
pwd:<input type="text" v-model="pwd"/><br/>
<input type="button" @click="axios02" value="发送JSON格式的参数值的异步请求"/>
</div>
</body>
</html>
将后端的java Object转化为json发送给前端
axiosServlet
@WebServlet("/axios03.do")
public class Axios03Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
StringBuffer stringBuffer = new StringBuffer("");
BufferedReader bufferedReader = request.getReader();
String str = null ;
while((str=bufferedReader.readLine())!=null){
stringBuffer.append(str);
}
str = stringBuffer.toString() ;
//已知 String
//需要转化成 Java Object
Gson gson = new Gson();
//Gson有两个API
//1.fromJson(string,T) 将字符串转化成java object
//2.toJson(java Object) 将java object转化成json字符串,这样才能响应给客户端
User user = gson.fromJson(str, User.class);
user.setUname("鸠摩智");
user.setPwd("123456");
//假设user是从数据库查询出来的,现在需要将其转化成json格式的字符串,然后响应给客户端
String userJsonStr = gson.toJson(user);
response.setCharacterEncoding("UTF-8");
//MIME-TYPE
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(userJsonStr);
}
}
前端页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>03.演示Axios发送异步请求给服务器端,服务器响应json格式的数据给客户端</title>
<script language="JavaScript" src="script/vue.js"></script>
<script language="JavaScript" src="script/axios.min.js"></script>
<script language="JavaScript">
window.onload=function(){
var vue = new Vue({
"el":"#div0",
data:{
uname:"lina",
pwd:"ok"
},
methods:{
axios03:function(){
axios({
method:"POST",
url:"axios03.do",
data:{
uname:vue.uname,
pwd:vue.pwd
}
})
.then(function (value) {
var data = value.data;
// data对应的数据:
// {uname:"鸠摩智",pwd:"ok"}
vue.uname=data.uname;
vue.pwd=data.pwd;
//此处value中的data返回的是 js object,因此可以直接点出属性
//如果我们获取的是一个字符串: "{uname:\"鸠摩智\",pwd:\"ok\"}"
//js语言中 也有字符串和js对象之间互转的API
//string JSON.stringify(object) object->string
//object JSON.parse(string) string->object
})
.catch(function (reason) {
console.log(reason);
});
}
}
});
}
</script>
</head>
<body>
<div id="div0">
uname:<input type="text" v-model="uname"/><br/>
pwd:<input type="text" v-model="pwd"/><br/>
<input type="button" @click="axios03" value="服务器响应json格式的数据给客户端"/>
</div>
</body>
</html>
js语言中 也有字符串和js对象之间互转的API
string JSON.stringify(object) object->string
object JSON.parse(string) string->object
这里展示一下JS里面转化字符串和Object
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>04.JS中的字符串和Object之间互转的API</title>
<script language="JavaScript">
function hello01(){
/*
//1. js string - > js object
var str = "{\"uname\":\"lina\",\"age\":20}";
var user = JSON.parse(str);
alert(typeof user);
alert(user.uname+"_"+user.age);
*/
//2. js object -> js string
var user = {"uname":"lina","age":99};
alert(typeof user);
var userStr = JSON.stringify(user);
alert(typeof userStr);
alert(userStr);
}
</script>
</head>
<body>
<div id="div0">
<input type="button" value="确定" onclick="hello01()"/>
</div>
</body>
</html>
AxiosServlet
@WebServlet("/axios03.do")
public class Axios03Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
StringBuffer stringBuffer = new StringBuffer("");
BufferedReader bufferedReader = request.getReader();
String str = null ;
while((str=bufferedReader.readLine())!=null){
stringBuffer.append(str);
}
str = stringBuffer.toString() ;
//已知 String
//需要转化成 Java Object
Gson gson = new Gson();
//Gson有两个API
//1.fromJson(string,T) 将字符串转化成java object
//2.toJson(java Object) 将java object转化成json字符串,这样才能响应给客户端
User user = gson.fromJson(str, User.class);
user.setUname("鸠摩智");
user.setPwd("123456");
//假设user是从数据库查询出来的,现在需要将其转化成json格式的字符串,然后响应给客户端
String userJsonStr = gson.toJson(user);
response.setCharacterEncoding("UTF-8");
//MIME-TYPE
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(userJsonStr);
}
}
4. 使用VUE和Axios改写书城项目
4.1 购物车模块改写
复制之前的书城项目为新模块,然后导入相应的包,添加框架支持。
这里改造一下DispatcherServlet,需要在返回字符串为空的时候,直接return,不渲染页面。
// 2. controller组件中的方法调用
method.setAccessible(true);
Object returnObj = method.invoke(controllerBeanObj, parameterValues); // “2”
// 3. 视图处理
String methodReturnStr = (String) returnObj;
if (StringUtil.isEmpty(methodReturnStr)) {
return;
}
if (methodReturnStr.startsWith("redirect:")) { //比如 "redirect:fruit.do"
String redirectStr = methodReturnStr.substring("redirect:".length());
resp.sendRedirect(redirectStr);
// 发送json数据
}else if(methodReturnStr.startsWith("json:")){
String jsonStr = methodReturnStr.substring("json:".length());
PrintWriter out = resp.getWriter();
out.print(jsonStr);
out.flush();
}else {
super.processTemplate(methodReturnStr, req, resp); //比如 "edit" "index"
}
}
我们此前客户端发送请求是通过DispatcherServlet分配,发给Controller,然后返回给Thymeleaf渲染,现在是直接访问html,然后通过axios发送ajax请求,然后通过vue实时的渲染。
这里我们以前访问购物车是直接访问购物车的Controller,然后默认使用index方法。
<a th:href="@{/cart.do}" class="cart iconfont icon-gouwuche ">
购物车
<div class="cart-num" th:text="${session.currUser.cart.getTotalCount()}">3</div>
</a>
现在我们想改成访问html,先改成这个样子,虽然还是经过thymeleaf渲染,但是我们一会会把前端的页面部分的thymeleaf语法都去除,css路径,图片路径这些可以先不改。
<a th:href="@{/page.do(operate='page', page='cart/cart')}" class="cart iconfont icon-gouwuche ">
购物车
<div class="cart-num" th:text="${session.currUser.cart.getTotalCount()}">3</div>
</a>
以前我们购物车的数据是经过CartController的index方法,然后把Session中用户的购物车列表通过thymeleaf打印,现在我们不用这种方法,先给图书的div写id,记得引入vue和axios,文件也复制过来。
<script language="JavaScript" th:src="@{/static/script/cart.js}"></script>
因为我们之前这个页面绑定html文件在cart.js,我们就在这里把vue添加上去。id写刚刚书写的#cart_div,而data暂时不填,先暂时写一个beforemount,然后methods里面写axios,method这里我们先写POST,而url我们写cart.do,通过.then在控制台输出参数验证是否做到了数据的获取,同时我们在数据装载的时候去调用一下getCart方法
function editCart(cartItemId, buyCount) {
window.location.href = 'cart.do?operate=editCart&cartItemId=' + cartItemId + '&buyCount=' + buyCount;
}
window.onload = function () {
var vue = new Vue({
el: "#cart_div",
data: {},
methods: {
getCart: function () {
axios({
method:"POST",
url: "cart.do",
params: {
operate:"cartInfo",
}
})
.then(function (value) {
console.log(value.data);
})
.catch(function (reason) {
});
}
},
mounted:function () {
this.getCart();
}
});
}
此前我们是index方法,直接从session获取数据,没有别的参数,这里为了展示axios传参,我们干脆直接写个新的方法cartInfo,这样就可以让axios这里写参数。
public class CartController {
private CartItemService cartItemService;
public String index(HttpSession session){
User user = (User) session.getAttribute("currUser");
Cart cart = cartItemService.getCart(user);
user.setCart(cart);
session.setAttribute("currUser", user);
return "cart/cart";
}
public String cartInfo(HttpSession session){
User user = (User) session.getAttribute("currUser");
Cart cart = cartItemService.getCart(user);
Gson gson = new Gson();
String cartJsonStr = gson.toJson(cart);
return "json:" + cartJsonStr;
}
public String addCart(Integer bookId, HttpSession session) {----}
public String editCart(Integer cartItemId, Integer buyCount) {----}
}
我们在这个地方设置断点,看看能不能进入这个方法
发现能进去,然后看axios回调函数返回的是什么数据,事实上这里data就是一个购物车,购物车有cartItemMap这个属性
那么我们就干脆在data中写一个cart数据类型,然后让回调函数返回的值赋值给这个类型的变量,这里一会用vue,一会用this,是因为在axios中的this是axios对象,而后面的mounted:function ()是vue中的,this就是vue对象。
window.onload = function () {
var vue = new Vue({
el: "#cart_div",
data: {
cart:{}
},
methods: {
getCart: function () {
axios({
method:"POST",
url: "cart.do",
params: {
operate:"cartInfo",
}
})
.then(function (value) {
var cart = value.data;
vue.cart = cart;
})
.catch(function (reason) {
});
}
},
mounted:function () {
this.getCart();
}
});
}
然后我们就能把之前thymeleaf改造的前端代码,改写为vue的,这里使用vue的for迭代cartItemMap,其实会出现一个问题,图片返回的路径是project下的直接的路径,但是我们实际放在static下的upload里面,需要以字符串拼接的形式返回。
改造前:
<table>
<thead>
<tr>
<th>图片</th>
<th>商品名称</th>
<th>数量</th>
<th>单价</th>
<th>金额</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr th:each="cartItem : ${session.currUser.getCart().getCartItemMap().values()}">
<td>
<img th:src="@{|/static/uploads/${cartItem.getBook().bookImg}|}" alt=""/>
</td>
<td th:text="${cartItem.getBook().bookName}">活着</td>
<td>
<span class="count" th:onclick="|editCart(${cartItem.id}, ${cartItem.buyCount - 1})|">-</span>
<input class="count-num" type="text" th:value="${cartItem.buyCount}"/>
<span class="count" th:onclick="|editCart(${cartItem.id}, ${cartItem.buyCount + 1})|">+</span>
</td>
<td th:text="${cartItem.getBook().price}">36.8</td>
<td th:text="${cartItem.xj}">36.8</td>
<td><a href="">删除</a></td>
</tr>
</tbody>
</table>
改造后
<table>
<thead>
<tr>
<th>图片</th>
<th>商品名称</th>
<th>数量</th>
<th>单价</th>
<th>金额</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="cartItem in cart.cartItemMap">
<td>
<img v-bind:src="'static/uploads/'+cartItem.book.bookImg" alt="" />
</td>
<td>{{cartItem.book.bookName}}</td>
<td>
<span class="count" @click="editCart(cartItem.id,cartItem.buyCount-1)">-</span>
<input class="count-num" type="text" value="1" v-bind:value="cartItem.buyCount"/>
<span class="count" @click="editCart(cartItem.id,cartItem.buyCount+1)">+</span>
</td>
<td>{{cartItem.book.price}}</td>
<td>{{cartItem.xj}}</td>
<td><a href="">删除</a></td>
</tr>
</tbody>
</table>
另外,我们返回的中文发现是乱码,这里其实因为我们之前设置的字符过滤器,在doFilter里面只给request设置了字符编码,没有给response设置,我们添加一下即可。
@WebFilter(urlPatterns = {"*.do"},
initParams = {@WebInitParam(name = "encoding", value = "UTF-8")})
public class CharacterEncodingFilter implements Filter {
private String encoding = "UTF-8";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
String encodingStr = filterConfig.getInitParameter("encoding");
if (StringUtil.isNotEmpty(encodingStr)) {
encoding = encodingStr;
}
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
((HttpServletRequest)servletRequest).setCharacterEncoding(encoding);
((HttpServletResponse)servletResponse).setCharacterEncoding(encoding);
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
当然如果不想改这部分也可以在返回json数据的时候提前在DispatcherServlet里设置。
// 2. controller组件中的方法调用
method.setAccessible(true);
Object returnObj = method.invoke(controllerBeanObj, parameterValues); // “2”
// 3. 视图处理
String methodReturnStr = (String) returnObj;
if (StringUtil.isEmpty(methodReturnStr)) {
return;
}
if (methodReturnStr.startsWith("redirect:")) { //比如 "redirect:fruit.do"
String redirectStr = methodReturnStr.substring("redirect:".length());
resp.sendRedirect(redirectStr);
// 发送json数据
}else if(methodReturnStr.startsWith("json:")){
resp.setCharacterEncoding("UTF-8");
resp.setContentType("application/json,charset=UTF-8");
String jsonStr = methodReturnStr.substring("json:".length());
PrintWriter out = resp.getWriter();
out.print(jsonStr);
out.flush();
}else {
super.processTemplate(methodReturnStr, req, resp); //比如 "edit" "index"
}
}
}
此时发现,金额没有获取到,这是因为之前Thymeleaf获取属性是直接通过get方法,而我们的xj专门给它写了get方法,这是数据库查不到的,并且是单独我们封装给查询的时候调用它,进行Bigdecimal乘法算出来的。
现在具体来看我们这个cartItemList到底是怎么查到赋值的,首先它来自于CartItemService的方法getCartItemList,从cartItemDAO层获取同一个user的全部cartItem的List,然后因为是通过反射获取的book字段,没有id值,所以还利用了BookService层的getBook方法把book的属性都查到并赋值给CartItem,然后再通过getCart封装到Map,最后赋值给Cart。这里从DAO查到的CartItem项肯定没有xj的值,是null,因为我们最后是通过Thymeleaf调用实时查出来的。因此我们放入Map前,可以在getCartItemList里面,查到Book值的时候人为调用这个Getxj的方法,然后就会给这个属性赋值,自然最后我们从session域取出来的时候,里面的cartItem都自带xj的值了。
public class CartItemServiceImpl implements CartItemService {
private CartItemDAO cartItemDAO = null;
private BookService bookService = null;
@Override
public void addCartItem(CartItem cartItem) {---}
@Override
public void addOrUpdateCartItem(CartItem cartItem, Cart cart) {---}
@Override
public List<CartItem> getCartItemList(User user) {
List<CartItem> cartItemList = cartItemDAO.getCartItemList(user);
for (CartItem cartItem : cartItemList) {
Book book = bookService.getBook(cartItem.getBook().getId());
cartItem.setBook(book);
//此处需要调用getXj(),目的是执行getXj()内部的代码
//让book的price乘以buyCount,从而计算出xj这个属性的值
cartItem.getXj();
}
return cartItemList;
}
@Override
public Cart getCart(User user) {
List<CartItem> cartItemList = getCartItemList(user);
Map<Integer, CartItem> cartItemMap = new HashMap<>();
for (CartItem cartItem : cartItemList) {
cartItemMap.put(cartItem.getBook().getId(), cartItem);
}
Cart cart = new Cart();
cart.setCartItemMap(cartItemMap);
return cart;
}
}
这里是xj的方法内部细节
public Double getXj() {
BigDecimal BigDecimalPrice = new BigDecimal(getBook().getPrice() + "");
BigDecimal BigDecimalBuyCount = new BigDecimal(getBuyCount() + "");
xj = BigDecimalPrice.multiply(BigDecimalBuyCount).doubleValue();
return BigDecimalPrice.multiply(BigDecimalBuyCount).doubleValue();
}
此时总金额就能正常显示了
以前我们的购物车编辑数量是同步的请求,现在我们改成异步的请求
function editCart(cartItemId, buyCount) {
window.location.href = 'cart.do?operate=editCart&cartItemId=' + cartItemId + '&buyCount=' + buyCount;
}
此前我们是直接写在外部的js函数,现在我们要写在vue内部,删掉外面同步的这个代码,我们为了达到实时刷新数据的目的,在这个方法被调用的时候后就重新调之前的getCart方法,让它实时再查一次,通过vue更新前端的数据
window.onload = function () {
var vue = new Vue({
el: "#cart_div",
data: {
cart:{}
},
methods: {
getCart: function () {
axios({
method:"POST",
url: "cart.do",
params: {
operate:"cartInfo",
}
})
.then(function (value) {
var cart = value.data;
vue.cart = cart;
})
.catch(function (reason) {
});
},
editCart:function (cartItemId, buyCount){
axios({
method:"POST",
url:"cart.do",
params: {
operate:"editCart",
cartItemId: cartItemId,
buyCount: buyCount
}
})
.then(function (value) {
vue.getCart();
})
.catch(function (reason) {
});
}
},
mounted:function () {
this.getCart();
}
});
}
这里后端我们更新后就不需要让它返回字符串再同步渲染了,我们通过ajax达到了目的,这里企业里可能会通过返回一个代码,然后通过代码能获取到底方法执行的怎么样
public class CartController {
private CartItemService cartItemService;
public String index(HttpSession session){---}
public String cartInfo(HttpSession session){---}
public String addCart(Integer bookId, HttpSession session) {---}
public String editCart(Integer cartItemId, Integer buyCount) {
cartItemService.updateCartItem(new CartItem(cartItemId, buyCount));
return "";
}
}
此时已经完成了点击按钮异步更新数据的功能,但是总金额和总数这里其实还是Thymeleaf渲染,所以是session下的数据,没有实时更新,同时也是中途调用get才能查到,换成vue需要和刚刚一样中途查询过程赋值
<div>共<span th:text="${session.currUser.cart.getTotalBookCount()}">3</span>件商品</div>
<div class="total-price">总金额<span th:text="${session.currUser.cart.getTotalMoney()}">99.9</span>元</div>
改为
<div>共<span>{{cart.totalBookCount}}</span>件商品</div>
<div class="total-price">总金额<span>{{cart.totalMoney}}</span>元</div>
老师这里get方法写到了controller层再去通过get方法赋值,但是这里感觉放在更底层的Service层更好,所以我放在了Service层的getCart里面
public class CartItemServiceImpl implements CartItemService {
private CartItemDAO cartItemDAO = null;
private BookService bookService = null;
@Override
public void addCartItem(CartItem cartItem) {---}
@Override
public void updateCartItem(CartItem cartItem) {---}
@Override
public void addOrUpdateCartItem(CartItem cartItem, Cart cart) {---}
@Override
public List<CartItem> getCartItemList(User user) {---}
@Override
public Cart getCart(User user) {
List<CartItem> cartItemList = getCartItemList(user);
Map<Integer, CartItem> cartItemMap = new HashMap<>();
for (CartItem cartItem : cartItemList) {
cartItemMap.put(cartItem.getBook().getId(), cartItem);
}
Cart cart = new Cart();
cart.setCartItemMap(cartItemMap);
//调用Cart中的三个属性的get方法,目的是在此处计算这三个属性的值,否则这三个属性为null,
//导致的结果就是下一步的gson转化时,为null的属性会被忽略
cart.getTotalBookCount();
cart.getTotalCount();
cart.getTotalMoney();
return cart;
}
}
此时彻底完成向vue的转换,完结撒花。