JavaWeb《后端内容:5. 项目实战:书城系统(下篇)》

目录

1. Cookie了解

Cookie在浏览器和服务器之间的传递

1. 创建Cookie对象 在客户端保存Cookie

2. 设置Cookie的有效时长

3. Cookie的应用

2. kaptcha

3. 注册功能实装

3.1 注册的初步实现

3.2 正则表达式了解

3.3 注册页面的表单验证

3.4 判断用户名是否已经被注册(原生Ajax方式)

1. Ajax

3. VUE和Axios

3.1 Vue入门

0. 定义对象的两种方式 

1 {{}} - 相当于innerText

2 v-bind:attr 绑定属性值

3 v-model 双向绑定

4 v-if , v-else , v-show

5 v-for 迭代

6 v-on 绑定事件

7 其他

8 生命周期

3.2 Axios入门

Axios是Ajax的一个框架,简化Ajax操作

2-2. 客户端向服务器发送JSON格式的数据

4. 使用VUE和Axios改写书城项目

4.1 购物车模块改写


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;">&nbsp;</div>-->
        <!--<br/>-->
        <!--<div v-else="num%2==0" style="width:200px;height:200px;background-color: coral">&nbsp;</div>-->


        <!-- v-show -->
        <!-- v-show是通过display属性来控制这个div是否显示 -->
        <div v-show="num%2==0" style="width:200px;height:200px;background-color:blueviolet;">&nbsp;</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的转换,完结撒花。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值