博客

普通会员

1

帖子

0

回复

4

积分

楼主
发表于 2020-04-17 11:05:34 | 查看: 224 | 回复: 0

第7章 Thymeleaf、Rabbitmq实现静态页

视频讲解

链接:https://pan.baidu.com/s/1b16MxXNOm-YRnWIINAIMYw
提取码:wy38


学习目标

  • Thymeleaf的介绍

  • Thymeleaf的入门

  • Thymeleaf的语法及标签

  • 商品详情页静态化工程搭建

  • ==商品详情页静态化功能实现==

  1.详情页静态化操作  2.填充基础数据  Spu、List<Sku>  3.规格切换

   

==搜索页面渲染==

  1.数据展示
  2.搜索条件展示
  3.实现条件搜索控制

   

  • 用户修改商品信息,同步更新创建商品详情页

1.Thymeleaf介绍

thymeleaf是一个XML/XHTML/HTML5模板引擎,可用于Web与非Web环境中的应用开发。它是一个开源的Java库,基于Apache License 2.0许可,由Daniel Fernández创建,该作者还是Java加密库Jasypt的作者。

Thymeleaf提供了一个用于整合Spring MVC的可选模块,在应用开发中,你可以使用Thymeleaf来完全代替JSP或其他模板引擎,如Velocity、FreeMarker等。Thymeleaf的主要目标在于提供一种可被浏览器正确显示的、格式良好的模板创建方式,因此也可以用作静态建模。你可以使用它创建经过验证的XML与HTML模板。相对于编写逻辑或代码,开发者只需将标签属性添加到模板中即可。接下来,这些标签属性就会在DOM(文档对象模型)上执行预先制定好的逻辑。

它的特点便是:开箱即用,Thymeleaf允许您处理六种模板,每种模板称为模板模式:

  • XML

  • 有效的XML

  • XHTML

  • 有效的XHTML

  • HTML5

  • 旧版HTML5

所有这些模式都指的是格式良好的XML文件,但*Legacy HTML5*模式除外,它允许您处理HTML5文件,其中包含独立(非关闭)标记,没有值的标记属性或不在引号之间写入的标记属性。为了在这种特定模式下处理文件,Thymeleaf将首先执行转换,将您的文件转换为格式良好的XML文件,这些文件仍然是完全有效的HTML5(实际上是创建HTML5代码的推荐方法)1

另请注意,验证仅适用于XML和XHTML模板。

然而,这些并不是Thymeleaf可以处理的唯一模板类型,并且用户始终能够通过指定在此模式下*解析*模板的方法和*编写*结果的方式来定义他/她自己的模式。这样,任何可以建模为DOM树(无论是否为XML)的东西都可以被Thymeleaf有效地作为模板处理。

2.Springboot整合thymeleaf

使用springboot 来集成使用Thymeleaf可以大大减少单纯使用thymleaf的代码量,所以我们接下来使用springboot集成使用thymeleaf.

实现的步骤为:

  • 创建一个sprinboot项目

  • 添加thymeleaf的起步依赖

  • 添加spring web的起步依赖

  • 编写html 使用thymleaf的语法获取变量对应后台传递的值

  • 编写controller 设置变量的值到model中

(1)创建工程

创建一个独立的工程springboot-thymeleaf,该工程为案例工程,不需要放到changgou-parent工程中。

pom.xml依赖

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itheima</groupId>
    <artifactId>springboot-thymeleaf</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
    </parent>

    <dependencies>
        <!--web起步依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--thymeleaf配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    </dependencies></project>

 

(2)创建html

在resources中创建templates目录,在templates目录创建 demo1.html,代码如下:

<!DOCTYPE html><html xmlns:th="http://www.thymeleaf.org"><head>
    <title>Thymeleaf的入门</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/></head><body><!--输出hello数据--><p th:text="${hello}"></p></body></html>

 

解释:

<html xmlns:th="http://www.thymeleaf.org">:这句声明使用thymeleaf标签

<p th:text="${hello}"></p>:这句使用 th:text=“${变量名}” 表示 使用thymeleaf获取文本数据,类似于EL表达式。

(3)修改application.yml配置

创建application.yml,并设置thymeleaf的缓存设置,设置为false。默认加缓存的,用于测试。

spring:  thymeleaf:    cache: false

 

在这里,其实还有一些默认配置,比如视图前缀:classpath:/templates/,视图后缀:.html

org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties部分源码如下:


(4)控制层

创建controller用于测试后台 设置数据到model中。

创建com.itheima.controller.TestController,代码如下:

@Controller@RequestMapping("/test")public class TestController {    /***
     * 访问/test/hello  跳转到demo1页面
     * @param model
     * @return
     */
    @RequestMapping("/hello")    public String hello(Model model){
        model.addAttribute("hello","hello welcome");        return "demo1";
    }
}

 

(5)测试

创建启动类com.itheima.ThymeleafApplication,代码如下:

@SpringBootApplicationpublic class ThymeleafApplication {    public static void main(String[] args) {
        SpringApplication.run(ThymeleafApplication.class,args);
    }
}

 

启动系统,并在浏览器访问

http://localhost:8080/test/hello

 


3 Thymeleaf基本语法

(1)th:action

定义后台控制器路径,类似<form>标签的action属性。

例如:

<form id="login-form" th:action="@{/test/hello}">
    <button>提交</button></form>

 

表示提交的请求地址为/test/hello

(2)th:each

对象遍历,功能类似jstl中的<c:forEach>标签。

创建com.itheima.model.User,代码如下:

public class User {    private Integer id;    private String name;    private String address;    //..get..set}

 

Controller添加数据

/***
 * 访问/test/hello  跳转到demo1页面
 * @param model
 * @return
 */@RequestMapping("/hello")public String hello(Model model){
    model.addAttribute("hello","hello welcome");    //集合数据
    List<User> users = new ArrayList<User>();
    users.add(new User(1,"张三","深圳"));
    users.add(new User(2,"李四","北京"));
    users.add(new User(3,"王五","武汉"));
    model.addAttribute("users",users);    return "demo1";
}

 

页面输出

<table>
    <tr>
        <td>下标</td>
        <td>编号</td>
        <td>姓名</td>
        <td>住址</td>
    </tr>
    <tr th:each="user,userStat:${users}">
        <td>
            下标:<span th:text="${userStat.index}"></span>,        </td>
        <td th:text="${user.id}"></td>
        <td th:text="${user.name}"></td>
        <td th:text="${user.address}"></td>
    </tr></table>

 

测试效果

index.png

(3)Map输出

后台添加Map

//Map定义Map<String,Object> dataMap = new HashMap<String,Object>();
dataMap.put("No","123");
dataMap.put("address","深圳");
model.addAttribute("dataMap",dataMap);

页面输出

<div th:each="map,mapStat:${dataMap}">
    <div th:text="${map}"></div>
    key:<span th:text="${mapStat.current.key}"></span><br/>
    value:<span th:text="${mapStat.current.value}"></span><br/>
    ==============================================</div>

 

测试效果

index.png

(4)数组输出

后台添加数组

//存储一个数组String[] names = {"张三","李四","王五"};
model.addAttribute("names",names);

 

页面输出

<div th:each="nm,nmStat:${names}">
    <span th:text="${nmStat.count}"></span><span th:text="${nm}"></span>
    ==============================================</div>

 

测试效果

(5)Date输出

后台添加日期

//日期model.addAttribute("now",new Date());

 

页面输出

<div>
    <span th:text="${#dates.format(now,'yyyy-MM-dd hh:ss:mm')}"></span></div>

 

测试效果

index.png

(6)th:if条件

后台添加年龄

//if条件model.addAttribute("age",22);

 

页面输出

<div>
    <span th:if="${(age>=18)}">终于长大了!</span></div>

 

测试效果

index.png

(7)使用javascript


java代码为:


(8) 字符拼接 使用||

后台代码:


模板:


4 搜索页面渲染

4.1 搜索分析


搜索页面要显示的内容主要分为3块。

1)搜索的数据结果

2)筛选出的数据搜索条件

3)用户已经勾选的数据条件

4.2 搜索实现


搜索的业务流程如上图,用户每次搜索的时候,先经过搜索业务工程,搜索业务工程调用搜索微服务工程,这里搜索业务工程单独挪出来的原因是它这里涉及到了模板渲染以及其他综合业务处理,以后很有可能会有移动端的搜索和PC端的搜索,后端渲染如果直接在搜索微服务中进行,会对微服务造成一定的侵入,不推荐这么做,推荐微服务独立,只提供服务,如果有其他页面渲染操作,可以搭建一个独立的消费工程调用微服务达到目的。

4.2.1 搜索工程搭建

(1)工程创建

在changgou-web工程中创建changgou-web-search工程,并在changgou-web的pom.xml中引入如下依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--feign-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <!--amqp-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency></dependencies>

 

(2)静态资源导入

将资源中的页面/前端页面/search.html拷贝到工程的resources/templates目录下,js、css等拷贝到static目录下,如下图:


(3)Feign创建

修改changgou-service-search-api,添加com.changgou.search.feign.SkuFeign,实现调用搜索,代码如下:

@FeignClient(name="search")@RequestMapping("/search")public interface SkuFeign {    /**
     * 搜索
     * @param searchMap
     * @return
     */
    @GetMapping
    Map search(@RequestParam(required = false) Map searchMap);
}

 

由于以后做搜索都是基于GET请求,所以我们需要将之前的搜索改成GET请求操作,修改changgou-service-search微服务的com.changgou.search.controller.SkuController里面的search方法,代码如下:


(4)changgou-web-search的pom.xml依赖

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>changgou-web</artifactId>
        <groupId>com.changgou</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>changgou-web-search</artifactId>

    <dependencies>
        <!--search API依赖-->
        <dependency>
            <groupId>com.changgou</groupId>
            <artifactId>changgou-service-search-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies></project>

 

(5)搜索调用

在changgou-web-search中创建com.changgou.search.controller.SkuController,实现调用搜索,代码如下:

@Controller@RequestMapping(value = "/search")public class SkuController {    @Autowired
    private SkuFeign skuFeign;    /**
     * 搜索
     * @param searchMap
     * @return
     */
    @GetMapping(value = "/list")    public String search(@RequestParam(required = false) Map searchMap, Model model){        //调用changgou-service-search微服务
        Map resultMap = skuFeign.search(searchMap);
        model.addAttribute("result",resultMap);        return "search";
    }
}

 

(6)启动类创建

修改changgou-web-search,添加启动类com.changgou.SearchWebApplication,代码如下:

@SpringBootApplication@EnableEurekaClient@EnableFeignClients(basePackages = "com.changgou.search.feign")public class SearchWebApplication {    public static void main(String[] args) {
        SpringApplication.run(SearchWebApplication.class,args);
    }
}

 

(7)application.yml配置文件

server:  port: 18088eureka:  client:
    service-url:      defaultZone: http://127.0.0.1:7001/eureka  instance:
    prefer-ip-address: truefeign:  hystrix:    enabled: truespring:  thymeleaf:    cache: false  application:    name: search-web  main:
    allow-bean-definition-overriding: true

 

(8)项目完整结构


在search.html的头部引入thymeleaf标签

<html xmlns:th="http://www.thymeleaf.org">

 

测试:http://localhost:18088/search,效果如下:


4.2.2 搜索数据填充

后端搜索到数据后,前端页面进行数据显示,显示的数据分为3部分

1)搜索的数据结果
2)筛选出的数据搜索条件
3)用户已经勾选的数据条件

 

4.2.3 关键字搜索

用户每次输入关键字的时候,直接根据关键字搜索,关键字搜索的数据会存储到result.rows中,页面每次根据result获取rows,然后循环输出即可,同时页面的搜索框每次需要回显搜索的关键词。

实现思路

1.前端表单提交搜索的关键词
2.后端根据关键词进行搜索
3.将搜索条件存储到Model中
4.页面循环迭代输出数据
5.搜索表单回显搜索的关键词

 

(1)后台搜索实现

修改SkuController的search方法,代码如下:

/**
 * 搜索
 * @param searchMap
 * @return
 */@GetMapping(value = "/list")public String search(@RequestParam(required = false) Map<String,String> searchMap, Model model){    //调用changgou-service-search微服务
    Map<String,Object> resultMap = skuFeign.search(searchMap);    //搜索数据结果
    model.addAttribute("result",resultMap);    //搜索条件
    model.addAttribute("searchMap",searchMap);    return "search";
}

 

(2)页面搜索实现

修改search.html


注意:搜索按钮为submit提交。

(3)页面结果输出

修改search.html,代码如下:


(4)测试

搜索华为关键字,效果如下:


4.3 搜索条件回显


搜索条件除了关键字外,还有分类、品牌、以及规格,这些在我们前面已经将数据存入到了Map中,我们可以直接从Map中将数据取出,然后在页面输出即可。

分类:result.categoryList

品牌:result.brandList

规格:result.specList

修改search.html的条件显示部分,代码如下:


上图代码如下:

<!--selector--><div class="clearfix selector">
    <div class="type-wrap" th:if="${#maps.containsKey(result, 'categoryList')}">
        <div class="fl key">分类</div>
        <div class="fl value">
            <span th:each="category,categoryStat:${result.categoryList}">
                <a th:text="${category}"></a>&nbsp;&nbsp;            </span>
        </div>
        <div class="fl ext"></div>
    </div>
    <div class="type-wrap logo" th:if="${#maps.containsKey(result, 'brandList')}">
        <div class="fl key brand">品牌</div>
        <div class="value logos">
            <ul class="logo-list">
                <li th:each="brand,brandStat:${result.brandList}">
                    <a th:text="${brand}"></a>
                </li>
            </ul>
        </div>
        <div class="ext">
            <a href="javascript:void(0);" class="sui-btn">多选</a>
            <a href="javascript:void(0);">更多</a>
        </div>
    </div>
    <div class="type-wrap" th:each="spec,specStat:${result.specList}" th:unless="${#maps.containsKey(searchMap, 'spec_'+spec.key)}">
        <div class="fl key" th:text="${spec.key}"></div>
        <div class="fl value">
            <ul class="type-list">
                <li th:each="op,opStat:${spec.value}">
                    <a th:text="${op}"></a>
                </li>
            </ul>
        </div>
        <div class="fl ext"></div>
    </div>

    <div class="type-wrap" th:unless="${#maps.containsKey(searchMap, 'price')}">
        <div class="fl key">价格</div>
        <div class="fl value">
            <ul class="type-list">
                <li>
                    <a th:text="0-500元"></a>
                </li>
                <li>
                    <a th:text="500-1000元"></a>
                </li>
                <li>
                    <a th:text="1000-1500元"></a>
                </li>
                <li>
                    <a th:text="1500-2000元"></a>
                </li>
                <li>
                    <a th:text="2000-3000元"></a>
                </li>
                <li>
                    <a th:text="3000元以上"></a>
                </li>
            </ul>
        </div>
        <div class="fl ext">
        </div>
    </div>
    <div class="type-wrap">
        <div class="fl key">更多筛选项</div>
        <div class="fl value">
            <ul class="type-list">
                <li>
                    <a>特点</a>
                </li>
                <li>
                    <a>系统</a>
                </li>
                <li>
                    <a>手机内存 </a>
                </li>
                <li>
                    <a>单卡双卡</a>
                </li>
                <li>
                    <a>其他</a>
                </li>
            </ul>
        </div>
        <div class="fl ext">
        </div>
    </div></div>

 

解释:

th:unless:条件不满足时,才显示${#maps.containsKey(result,'brandList')}:map中包含某个key

 

4.4 条件搜索实现


用户每次点击搜索的时候,其实在上次搜索的基础之上加上了新的搜索条件,也就是在上一次请求的URL后面追加了新的搜索条件,我们可以在后台每次拼接组装出上次搜索的URL,然后每次将URL存入到Model中,页面每次点击不同条件的时候,从Model中取出上次请求的URL,然后再加上新点击的条件参数实现跳转即可。

(1)后台记录搜索URL

修改SkuController,添加组装URL的方法,并将组装好的URL存储起来,代码如下:


(2)页面搜索对接



th:href 这里是超链接的语法,例如:th:href="@{${url}(price='500-1000')}"表示请求地址是取url参数的值,同时向后台传递参数price的值为500-100元。

4.5 移除搜索条件


如上图,用户点击条件搜索后,要将选中的条件显示出来,并提供移除条件的x按钮,显示条件我们可以从searchMap中获取,移除其实就是将之前的请求地址中的指定条件删除即可。

(1)条件显示

修改search.html,代码如下:


解释:

${#strings.startsWith(sm.key,'spec_')}:表示以spec_开始的key${#strings.replace(sm.key,'spec_','')}:表示将sm.key中的spec_替换成空

 

(2)移除搜素条件

修改search.html,移除分类、品牌、价格、规格搜索条件,代码如下:


4.6 排序(作业)


上图代码是排序代码,需要2个属性,sortRule:排序规则,ASC或者DESC,sortField:排序的域,前端每次只需要将这2个域的值传入到后台即可实现排序。

(1)后台组装排序URL

每次排序的时候恢复第1页查询,所以url地址我们需要重新拼接,每次切换排序的时候,不需要之前的排序信息,修改SkuController,代码如下:


代码如下:

private String url(Map<String, String> searchMap) {// { spec_网络:"移动4G","keywords":"华为"}
    String url = "/search/list"; // a/b?id=1&
    if (searchMap != null) {
        url += "?";        for (Map.Entry<String, String> stringStringEntry : searchMap.entrySet()) {            //如果是排序 则 跳过 拼接排序的地址 因为有数据
            if(stringStringEntry.getKey().equals("sortField") || stringStringEntry.getKey().equals("sortRule")){                continue;
            }
            url += stringStringEntry.getKey() + "=" + stringStringEntry.getValue() + "&";

        }        if(url.lastIndexOf("&")!=-1)
            url = url.substring(0, url.lastIndexOf("&"));
    }    return url;
}

 

(2)前端排序实现

修改search.html,实现排序,代码如下:


这一块我们实现了价格排序,同学们课后去实现以下销量和新品排序。



本帖子中包含资源

您需要 登录 才可以下载,没有帐号?立即注册

您需要登录后才可以回帖 登录 | 立即注册

2018-2020 ©v2.1 冀ICP备19027484号-1

点击这里给我发消息