前言
在上一期的博客分享中,我们一起了解了有关SpringBoot的一些理论知识以及简单的运用方法。今天我们进一步的了解,今天给大家分享的是Freemarker的基础使用及案例演示。请认真仔细观看哦
一、FreeMarker简介
1. 概述(什么是FreeMarker)
FreeMarker是一款模板引擎,即一种基于模板和要改变的数据,并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
模板编写为FreeMarker Template Language (FTL)。它是简单的,专用的语言, 不是 像PHP那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。
图示
FreeMarker最初的设计,是被用来在MVC模式的Web开发框架中生成HTML页面的,它没有被绑定到 Servlet或HTML或任意Web相关的东西上。它也可以用于非Web应用环境中。
2. Freemarker模板组成部分
FreeMarker模板文件主要由以下四个部分构成:
- 文本:直接输出的部分。
- 注释:即<#-- …… -->格式部分,不会输出。
- 插值:即${…… }或者#{…… }格式的部分,将使用数据模型中的部分替代输出。
- FTL指令:FreeMarker指令,和HTML标记类似,名字前加#予以区分,不会输出。
延伸
FreeMarker与Web容器无关
FreeMarker与Web容器无关,即在Web运行时,它并不知道Servlet或HTTP,故此FreeMarker不仅可以用作表现层的实现技术,而且还可以用于生成XML,JSP或Java等各种文本文件。
在Java Web领域,FreeMarker是应用广泛的模板引擎,主要用于MVC中的view层,生成html展示数据给客户端,可以完全替代JSP。
总之,FreeMarker是一个模板引擎,一个基于模板生成文本输出的通用工具,使用纯Java编写,模板中没有业务逻辑,外部Java程序通过数据库操作等生成数据传入模板(template)中,然后输出页面。它能够生成各种文本:HTML、XML、RTF、Java源代码等等,而且不需要Servlet环境,并且可以从任何源载入模板,如本地文件、数据库等等。
3. FreeMarker特点及优势
FreeMarker主要的特有及优势如下表所示
特点及优势 | 详细说明 |
模板与逻辑分离 | FreeMarker将模板与业务逻辑彻底分离,这使得页面内容更加清晰,也方便了后期的修改和维护。 |
提高开发效率 | 与JSP相比,FreeMarker模板技术不需要编译和转换,因此在开发过程中,可以减少等待时间,提高开发效率。 |
明确人员分工 | 使用FreeMarker后,界面开发人员和程序开发人员可以更加明确地分工,各自专注于自己的领域,提高工作效率。 |
Freemarker还有一些特点或者优势会在一些指定的应用场景或者开发中会体现出来,根据使用的情况而定。
二、SpringBoot整合Freemarker
1. 配置
1.1 pom文件配置
修改pom.xml文件,添加以下配置:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
1.2 yml文件配置
修改application.yml或application.properties文件,添加以下配置:
# 配置Freemarker
freemarker:
# 设置模板后缀名
suffix: .ftl
# 设置文档类型
content-type: text/html
# 设置页面编码格式
charset: UTF-8
# 设置页面缓存
cache: false
# 设置ftl文件路径
template-loader-path: classpath:/templates
# 设置静态文件路径,js,css等
mvc:
static-path-pattern: /static/**
1.3 配置Freemarker文件模版
首先在resources文件夹下创建一个templates文件存放Freemarker文件,然后到settings -> Editor -> File And Code Templates中进行模版配置。
当我们在指定的文件夹下新建项时我们就会发现我们配置好的Freemarker文件
如果我们需要访问我们新建好的Freemarker文件不能直接访问,需要编写一个控制类来控制访问跳转
FreemarkerController
package com.yx.sboot.Controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
//没有使用RestController,因为只是控制页面的跳转而不是数据显示和修改数据
@Controller
public class FreemarkerController {
@RequestMapping("/")
public String home(){
return "index";
}
}
然后我们启动项目访问即可。
延伸
以下是几种传参方式
2. Freemarker数据类型
2.1 字符串
在文本中确定字符串值的方法是看双引号,比如:
"some text"
,或单引号,比如:'some text'
。这两种形式是等同的。 如果文本自身包含用于字符引用的引号 ("
或'
)或反斜杠时, 应该在它们的前面再加一个反斜杠;这就是转义。 转义允许直接在文本中输入任何字符, 也包括换行。
示例:
${"It's \"quoted\" and
this is a backslash: \\"}
字符串类型处理:
方法 | 含义 |
---|---|
?substring(start,end) | 截取字符串(左闭右开) |
?uncap_first | 首字母小写输出 |
?cap_first | 首字母大写输出 |
?lower_case | 字母转小写输出 |
?upper_case | 字母转大写输出 |
?length | 获取字符串长度 |
?starts_with("xx")?string | 是否以指定字符开头(boolean类型) |
?ends_with("xx")?string | 是否以指定字符结尾(boolean类型) |
?index_of("xx") | 获取指定字符的索引 |
?trim | 去除字符串前后空格 |
?replace("xx","xx") | 替换指定字符串 |
示例:
?substring(start,end)的用法
?length的用法
在这里就简单的实现几个案例演示,详细的可以查询Freemarker的官网阅读使用。
官网: FreeMarker 中文官方参考手册 (foofun.cn)
延伸:
FreeMarker 的变量必须赋值,否则就会抛出异常。而对于 FreeMarker 来说,null 值和不存在的变量是完全一样的,因为 FreeMarker 无法理解 null 值。
<#-- 如果值不存在,直接输出会报错 -->
<#--${str}-->
<#-- 使用!,当值不存在时,默认显示空字符串 -->
${str!}<br>
<#-- 使用!"xx",当值不存在时,默认显示指定字符串 -->
${str!"这是一个默认值"}<br>
<#-- 使用??,判断字符串是否为空;返回布尔类型。如果想要输出,需要将布尔类型转换成字符串 -->
${(str??)?string}<br>
注: ?c ==>??string
2.2 数值
输入不带引号的数字就可以直接指定一个数字, 必须使用点作为小数的分隔符而不能是其他的分组分隔符。
数值类型处理:
方法 | 说明 |
?c/?string | 将数值转换成字符串输出 |
?string.currency | 将数值转换成货币类型的字符串输出 |
?string.percent | 将数值转换成百分比类型的字符串输出 |
?string["0.##"] | 将浮点型数值保留指定小数位输出 (##表示保留两位小数) |
示例:
页面显示
2.3 布尔型
直接写
true
或者false
就表示一个布尔值了,不需使用引号。注:
在freemarker中布尔类型不能直接输出;如果输出要先转成字符串
${flag?c}<br>
${flag?string}<br>
${flag?string("yes","no")}<br>
2.4 日期
日期变量可以存储和日期/时间相关的数据。
在freemarker中日期类型不能直接输出;如果输出要先转成日期型或字符串
日期格式输出:
输出方式 | 说明 |
---|---|
?date | 年月日 |
?time | 时分秒 |
?datetime | 年月日时分秒 |
?string("自定义格式") | 指定格式 |
案例演示
3. Freemarker常见指令
3.1 处理不存在的值
当试图访问一个不存在的变量时, FreeMarker 将会报错而导致模板执行中断。 通常我们可以使用两个特殊操作符来压制这个错误,控制这种错误情况。被控制的变量可以是顶层变量,哈希表或序列的子变量。 此外这些操作符还能处理方法调用的返回值不存在的情况。
-
默认值操作符
使用形式:
unsafe_expr!default_expr
或unsafe_expr!
or(unsafe_expr)!default_expr
或(unsafe_expr)!
这个操作符允许你为可能不存在的变量指定一个默认值。
${message!"default Value."}
<#assign message="Zking">
${message!"default Value."}
输出结果如下:
如果默认值被省略了,那么结果将会是空串,空序列或空哈希表。(这是 FreeMarker 允许多类型值的体现)请注意,如果想让默认值为
0
或false
,则不能省略它。
(${message!})
<#assign message = "Zking">
(${message!})
-
不存在值检测操作符
使用形式: unsafe_expr??
或 (unsafe_expr)??
这个操作符告诉我们一个值是否存在。基于这种情况, 结果是
true
或false
。
3.2 assign
使用该指令你可以创建一个新的变量, 或者替换一个已经存在的变量。语法格式如下:
案例
<#-- 创建一个str的变量 -->
<#assign str="hello">
<#-- 输出str -->
${str} <br>
<#-- 一次创建多个变量 -->
<#assign num=1 names=["zhangsan","lisi","wangwu"] >
${num} -- ${names?join(",")}
页面输出结果
3.3 if/elseif/else
使用格式如下
<#if condition>
...
<#elseif condition2>
...
<#elseif condition3>
...
...
<#else>
...
</#if>
3.4 list
使用格式如下
<#list sequence as item>
Part repeated for each item
<#else>
Part executed when there are 0 items
</#list>
3.5 include
<#include path>
或
<#include path options>
if/elseif/else、list、include会在后面的项目案例中详细的演示
三、项目案例演示
1. 公共类编写
将我们所需的css/js文件导入到我们的static文件夹中
common.ftl
<#--绝对路径-->
<#assign ctx="${springMacroRequestContext.contextPath}">
<link rel="stylesheet" href="${ctx}/bootstrap-3.4.1-dist/css/bootstrap.css">
<script src="${ctx}/jquery-3.6.1.js"></script>
<script src="${ctx}/bootstrap-3.4.1-dist/js/bootstrap.js"></script>
2. Sql语句的编写
对其数据表生成其实体类,对其实体类编写增删改查四个功能的SQL句。
TBookMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yx.sboot.mapper.TBookMapper">
<insert id="add">
insert into t_book
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">id,</if>
<if test="bookname != null">bookname,</if>
<if test="price != null">price,</if>
<if test="booktype != null">booktype,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if>
<if test="bookname != null">#{bookname},</if>
<if test="price != null">#{price},</if>
<if test="booktype != null">#{booktype},</if>
</trim>
</insert>
<!-- 修改 -->
<update id="updateById">
update t_book
<set>
<if test="bookname != null">
bookname = #{bookname},
</if>
<if test="price != null">
price = #{price},
</if>
<if test="booktype != null">
booktype = #{booktype},
</if>
</set>
where id = #{id}
</update>
<!-- 删除 -->
<delete id="delByIdInt">
delete from t_book
where id = #{id,jdbcType=INTEGER}
</delete>
<!-- 查询所有的方法 -->
<select id="list" resultType="tBook">
select * from t_book
</select>
<!-- 根据id查询 -->
<select id="listByID" resultType="tBook">
select * from t_book where id=#{id}
</select>
<!-- <resultMap id="BaseResultMap" type="com.yx.sboot.pojo.TBook">-->
<!-- <id property="id" column="id" jdbcType="INTEGER"/>-->
<!-- <result property="bookname" column="bookname" jdbcType="VARCHAR"/>-->
<!-- <result property="price" column="price" jdbcType="FLOAT"/>-->
<!-- <result property="booktype" column="booktype" jdbcType="VARCHAR"/>-->
<!-- </resultMap>-->
<!-- <sql id="Base_Column_List">-->
<!-- id,bookname,price,-->
<!-- booktype-->
<!-- </sql>-->
<!-- <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">-->
<!-- select-->
<!-- <include refid="Base_Column_List" />-->
<!-- from t_book-->
<!-- where id = #{id,jdbcType=INTEGER} -->
<!-- </select>-->
<!-- <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">-->
<!-- delete from t_book-->
<!-- where id = #{id,jdbcType=INTEGER} -->
<!-- </delete>-->
<!-- <insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.yx.sboot.pojo.TBook" useGeneratedKeys="true">-->
<!-- insert into t_book-->
<!-- ( id,bookname,price-->
<!-- ,booktype)-->
<!-- values (#{id,jdbcType=INTEGER},#{bookname,jdbcType=VARCHAR},#{price,jdbcType=FLOAT}-->
<!-- ,#{booktype,jdbcType=VARCHAR})-->
<!-- </insert>-->
<!-- <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="com.yx.sboot.pojo.TBook" useGeneratedKeys="true">-->
<!-- insert into t_book-->
<!-- <trim prefix="(" suffix=")" suffixOverrides=",">-->
<!-- <if test="id != null">id,</if>-->
<!-- <if test="bookname != null">bookname,</if>-->
<!-- <if test="price != null">price,</if>-->
<!-- <if test="booktype != null">booktype,</if>-->
<!-- </trim>-->
<!-- <trim prefix="values (" suffix=")" suffixOverrides=",">-->
<!-- <if test="id != null">#{id,jdbcType=INTEGER},</if>-->
<!-- <if test="bookname != null">#{bookname,jdbcType=VARCHAR},</if>-->
<!-- <if test="price != null">#{price,jdbcType=FLOAT},</if>-->
<!-- <if test="booktype != null">#{booktype,jdbcType=VARCHAR},</if>-->
<!-- </trim>-->
<!-- </insert>-->
<!-- <update id="updateByPrimaryKeySelective" parameterType="com.yx.sboot.pojo.TBook">-->
<!-- update t_book-->
<!-- <set>-->
<!-- <if test="bookname != null">-->
<!-- bookname = #{bookname,jdbcType=VARCHAR},-->
<!-- </if>-->
<!-- <if test="price != null">-->
<!-- price = #{price,jdbcType=FLOAT},-->
<!-- </if>-->
<!-- <if test="booktype != null">-->
<!-- booktype = #{booktype,jdbcType=VARCHAR},-->
<!-- </if>-->
<!-- </set>-->
<!-- where id = #{id,jdbcType=INTEGER} -->
<!-- </update>-->
<!-- <update id="updateByPrimaryKey" parameterType="com.yx.sboot.pojo.TBook">-->
<!-- update t_book-->
<!-- set -->
<!-- bookname = #{bookname,jdbcType=VARCHAR},-->
<!-- price = #{price,jdbcType=FLOAT},-->
<!-- booktype = #{booktype,jdbcType=VARCHAR}-->
<!-- where id = #{id,jdbcType=INTEGER} -->
<!-- </update>-->
</mapper>
3. 业务逻辑层
TBookMapper.java
package com.yx.sboot.mapper;
import com.yx.sboot.pojo.TBook;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @author 86158
* @description 针对表【t_book(�鱾��Ϣ��)】的数据库操作Mapper
* @createDate 2023-12-12 16:59:05
* @Entity com.yx.sboot.pojo.TBook
*/
@Repository
public interface TBookMapper {
// 查询所有
List<TBook> list();
// 根据id查询数据
TBook listByID(Integer id);
// 删除
int delByIdInt(TBook tBook);
// 修改
int updateById(TBook tBook);
// 新增
int add(TBook tBook);
}
TBookService.java
package com.yx.sboot.Service;
import com.yx.sboot.pojo.TBook;
import com.yx.sboot.util.PageBean;
import java.util.List;
/**
* @author 86158
* @description 针对表【t_book(�鱾��Ϣ��)】的数据库操作Mapper
* @createDate 2023-12-12 16:59:05
* @Entity com.yx.sboot.pojo.TBook
*/
public interface TBookService {
// 查询所有
List<TBook> list(PageBean pageBean);
// 查询所有
List<TBook> list();
// 根据id查询数据
TBook listByID(Integer id);
// 删除
int delByIdInt(TBook tBook);
// 修改
int updateById(TBook tBook);
// 新增
int add(TBook tBook);
}
TBookServiceImpl.java
package com.yx.sboot.Service.Impl;
import com.yx.sboot.Service.TBookService;
import com.yx.sboot.aop.PageAnnotation;
import com.yx.sboot.mapper.TBookMapper;
import com.yx.sboot.pojo.TBook;
import com.yx.sboot.util.PageBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class TBookServiceImpl implements TBookService {
@Autowired
private TBookMapper tBookMapper;
@Override
@PageAnnotation
public List<TBook> list(PageBean pageBean) {
return tBookMapper.list();
}
@Override
public List<TBook> list() {
return tBookMapper.list();
}
@Override
public TBook listByID(Integer id) {
return tBookMapper.listByID(id);
}
@Override
public int delByIdInt(TBook tBook) {
return tBookMapper.delByIdInt(tBook);
}
@Override
public int updateById(TBook tBook) {
return tBookMapper.updateById(tBook);
}
@Override
public int add(TBook tBook) {
return tBookMapper.add(tBook);
}
}
FreemarkerController.java
package com.yx.sboot.Controller;
import com.yx.sboot.Service.TBookService;
import com.yx.sboot.pojo.TBook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
//没有使用RestController,因为只是控制页面的跳转而不是数据显示和修改数据
@Controller
public class FreemarkerController {
@Autowired
private TBookService tBookService;
// 跳转主页
// 需要携带数据利用Model
@RequestMapping("/")
public String home(Model model){
List<TBook> list = this.tBookService.list();
model.addAttribute("book",list);
return "index";
}
// 删除
@RequestMapping("/del")
public String del(TBook tBook){
this.tBookService.delByIdInt(tBook);
return "redirect:/";
}
// 新增
@RequestMapping("/add")
public String add(TBook tBook){
this.tBookService.add(tBook);
return "redirect:/";
}
// 修改
@RequestMapping("/upd")
public String upd(TBook tBook){
this.tBookService.updateById(tBook);
return "redirect:/";
}
// // 根据id获取信息
// @RequestMapping("/selet/{id}")
// public String selet(@PathVariable("id") Integer id,Model model){
System.out.println(id);
// TBook tBook1 = this.tBookService.listByID(id);
// model.addAttribute("tbook",tBook1);
// return "/";
// }
// 多路径传参
@RequestMapping("/{dir}/{path}")
public String home(@PathVariable("dir") String dir,@PathVariable("path") String path){
return path;
}
}
4.页面显示层
index.ftl
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>君易书城</title>
<#-- 引入公共资源 -->
<#include "common.ftl">
</head>
<body>
<h1> 君易--鑨书城</h1>
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary" onclick="add_show()"> 新增书籍</button>
<!-- 新增Modal -->
<div class="modal fade" id="addmodal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">新增书籍界面</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="exampleInputEmail1">书籍名称</label>
<input type="text" class="form-control" id="bookname" aria-describedby="emailHelp">
</div>
<div class="form-group">
<label for="exampleInputPassword1">书籍类型</label>
<select class="form-control" id="booktype">
<option>玄幻</option>
<option>爱情</option>
<option>文学</option>
<option>历史</option>
<option>科幻</option>
</select>
</div>
<div class="form-group">
<label for="exampleInputPassword1">书籍价格</label>
<input type="text" class="form-control" id="price">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="closesub()" >关闭</button>
<button type="button" class="btn btn-primary" onclick="submit()">确认</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="updmodal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">修改书籍界面</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="exampleInputEmail1">书籍ID</label>
<input type="text" class="form-control" id="uid" aria-describedby="emailHelp" disabled>
</div>
<div class="form-group">
<label for="exampleInputEmail1">书籍名称</label>
<input type="text" class="form-control" id="ubookname" aria-describedby="emailHelp" >
</div>
<div class="form-group">
<label for="exampleInputPassword1">书籍类型</label>
<select class="form-control" id="ubooktype">
<option>玄幻</option>
<option>爱情</option>
<option>文学</option>
<option>历史</option>
<option>科幻</option>
</select>
</div>
<div class="form-group">
<label for="exampleInputPassword1">书籍价格</label>
<input type="text" class="form-control" id="uprice">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" onclick="updsubmit()" >确认修改</button>
</div>
</div>
</div>
</div>
<script>
function closesub(){
$("#bookname").val("");
$("#booktype").val("");
$("#price").val("");
$("#addmodal").modal('hide');
}
// 点击修改按钮
function updbook(id){
var id=id;
var uid='#upd'+id;
// 获取按钮元素
var button = $(uid);
// 获取按钮的父节点
var parentNode = button.parent();
// 获取父节点的兄弟节点
var siblings = parentNode.siblings();
// 遍历兄弟节点并获取文本
var text = '';
siblings.each(function() {
text += $(this).text()+","; // 获取兄弟节点的文本并拼接
});
var result= text.split(",")
var id=result[0];
var bookname=result[1];
var booktype=result[2];
var price=result[3].substring(1);
// 文本赋值
$("#ubookname").val(bookname);
$("#ubooktype").val(booktype);
$("#uprice").val(price);
$("#uid").val(id);
$("#updmodal").modal('show');
}
function updsubmit(){
var id= $("#uid").val();
var bookname = $("#ubookname").val();
var booktype = $("#ubooktype").val();
var price = $("#uprice").val();
let book={
id:id,
bookname:bookname,
booktype:booktype,
price:price
}
let url="${ctx}/upd";
$.post(
url,
book,
function (){
alert("修改成功");
$("#updmodal").modal('hide');
window.location.reload();
}
)
// console.log(id+""+booktype+""+price+""+bookname);
}
function add_show(){
// 打开新增模态框
$("#addmodal").modal('show');
}
// 新增的提交事件
function submit(){
var bookname = $("#bookname").val();
var booktype = $("#booktype").val();
var price = $("#price").val();
console.log(booktype+""+price+""+bookname);
let book={
bookname:bookname,
booktype:booktype,
price:price
}
let url="${ctx}/add"
$.post(
url,
book,
function (){
alert("新增成功");
$("#addmodal").modal('hide');
window.location.reload();
}
)
}
<#-- 获取表单数据-->
// $(document).ready(function() {
// $('#myForm').submit(function(event) {
// event.preventDefault(); // 防止表单默认提交行为
// var bookname = $('#bookname').val(); // 获取书籍名字输入框的值
// var booktype = $('#booktype').val(); // 获取书籍类型下拉框的值
// var price=$('price').val();
// // TODO: 在这里添加新增或修改的逻辑代码
// console.log(bookname+""+booktype+""+price);
// });
// });
</script>
<#--判断是否有数据-->
<#--有数据才显示表格-->
<#if book??>
<table class="table table-striped">
<tr>
<td>书籍编号</td>
<td>书籍名称</td>
<td>书籍类型</td>
<td>书籍价格</td>
<td>操作</td>
</tr>
<#-- 遍历集合显示数据-->
<#list book as b>
<tr>
<td>${b.id}</td>
<td>${b.bookname}</td>
<td>${b.booktype}</td>
<td>${b.price?string.currency}</td>
<td>
<a href="${ctx}/del?id=${b.id}">删除</a>
<button type="button" class="btn btn-primary" onclick="updbook(${b.id})" id="upd${b.id}"> 修改</button>
</td>
</tr>
</#list>
</table>
</#if>
<#--<#–String–>-->
<#--${"你好,君易,欢迎使用!!"?substring(1,4)}-->
<#--<br>-->
<#--${"你好,君易,欢迎使用!!"?substring(2)}-->
<#--<br>-->
<#--${"你好,君易,欢迎使用!!"?length}-->
<#--<br>-->
<#--${age!}-->
<#--<br>-->
<#--${age!"年龄不存在"}-->
<#--<br>-->
<#--${(age??)?c}-->
<#--<#–数值–>-->
<#--${10000?c}-->
<#--<br>-->
<#--${10000?string.currency}-->
<#--<br>-->
<#--${0.58?string.percent}-->
<#--<br>-->
<#--${0.545255525?string["0.###"]}-->
<#--<#–布尔型–>-->
<#--${flag?c}<br>-->
<#--${flag?string}<br>-->
<#--${flag?string("yes","no")}<br>-->
<#--<#–日期型–>-->
<#--${today?date}<br>-->
<#--${today?time}<br>-->
<#--${today?datetime}<br>-->
<#--${today?string("yyyy年MM月dd日 HH:mm:ss")}-->
<#--常见命令-->
<#--${message!"default Value."}-->
<#--<#assign message="Zking">-->
<#--${message!"default Value."}-->
<#-- 创建一个str的变量 -->
<#--<#assign str="hello">-->
<#--<#– 输出str –>-->
<#--${str} <br>-->
<#--<#– 一次创建多个变量 –>-->
<#--<#assign num=1 names=["zhangsan","lisi","wangwu"] >-->
<#--${num} -- ${names?join(",")}-->
</body>
</html>
效果演示
本期博客分享到此结束
感谢老铁们的观看
三连加关注支持博主哦