【SpringBoot】进阶之Freemarker运用(一起探索freemarker的奥秘吧)


前言

        在上一期的博客分享中,我们一起了解了有关SpringBoot的一些理论知识以及简单的运用方法。今天我们进一步的了解,今天给大家分享的是Freemarker的基础使用及案例演示。请认真仔细观看哦

 一、FreeMarker简介

1. 概述(什么是FreeMarker)

        FreeMarker是一款模板引擎,即一种基于模板和要改变的数据,并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。

        模板编写为FreeMarker Template Language (FTL)。它是简单的,专用的语言, 不是 像PHP那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。

图示

        FreeMarker最初的设计,是被用来在MVC模式的Web开发框架中生成HTML页面的,它没有被绑定到 Servlet或HTML或任意Web相关的东西上。它也可以用于非Web应用环境中。 

 2. Freemarker模板组成部分

FreeMarker模板文件主要由以下四个部分构成:

  1. 文本:直接输出的部分。
  2. 注释:即<#-- …… -->格式部分,不会输出。
  3. 插值:即${…… }或者#{…… }格式的部分,将使用数据模型中的部分替代输出。
  4. 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的特点及优势
特点及优势详细说明
模板与逻辑分离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_exprunsafe_expr! or (unsafe_expr)!default_expr(unsafe_expr)!

        这个操作符允许你为可能不存在的变量指定一个默认值。

${message!"default Value."}
<#assign message="Zking">
${message!"default Value."}

输出结果如下:

 

        如果默认值被省略了,那么结果将会是空串,空序列或空哈希表。(这是 FreeMarker 允许多类型值的体现)请注意,如果想让默认值为 0false,则不能省略它。

(${message!})
<#assign message = "Zking">
(${message!})
  • 不存在值检测操作符

        使用形式: unsafe_expr??(unsafe_expr)??

这个操作符告诉我们一个值是否存在。基于这种情况, 结果是 truefalse

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">&times;</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">&times;</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>
<#--&lt;#&ndash;String&ndash;&gt;-->
<#--${"你好,君易,欢迎使用!!"?substring(1,4)}-->
<#--<br>-->
<#--${"你好,君易,欢迎使用!!"?substring(2)}-->
<#--<br>-->
<#--${"你好,君易,欢迎使用!!"?length}-->
<#--<br>-->
<#--${age!}-->
<#--<br>-->
<#--${age!"年龄不存在"}-->
<#--<br>-->
<#--${(age??)?c}-->


<#--&lt;#&ndash;数值&ndash;&gt;-->
<#--${10000?c}-->
<#--<br>-->
<#--${10000?string.currency}-->
<#--<br>-->
<#--${0.58?string.percent}-->
<#--<br>-->
<#--${0.545255525?string["0.###"]}-->

<#--&lt;#&ndash;布尔型&ndash;&gt;-->
<#--${flag?c}<br>-->
<#--${flag?string}<br>-->
<#--${flag?string("yes","no")}<br>-->

<#--&lt;#&ndash;日期型&ndash;&gt;-->
<#--${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">-->
<#--&lt;#&ndash; 输出str &ndash;&gt;-->
<#--${str} <br>-->
<#--&lt;#&ndash; 一次创建多个变量 &ndash;&gt;-->
<#--<#assign num=1 names=["zhangsan","lisi","wangwu"] >-->
<#--${num} -- ${names?join(",")}-->



</body>
</html>

效果演示

 本期博客分享到此结束
感谢老铁们的观看
三连加关注支持博主哦

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/243830.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[OpenWrt]RAX3000一根线实现上网和看IPTV

背景&#xff1a; 1.我家电信宽带IPTV 2.入户光猫&#xff0c;桥接模式 3.光猫划分vlan&#xff0c;将上网信号IPTV信号&#xff0c;通过lan口&#xff08;问客服要光猫超级管理员密码&#xff0c;具体教程需要自行查阅&#xff0c;关键是要设置iptv在客户侧的vlan id&#…

Linux完成mysql数据库的备份与恢复

背景&#xff1a; 在进行数据报表的测试过程中&#xff0c;为了让我们的测试数据更加真实&#xff0c;因此我们需要同步生产数据到测试环境。方式有很多种&#xff0c;我这里介绍的是通过Linux完成数据同步。 备份数据&#xff1a; 执行命令&#xff1a;mysqldump -uxxx -pxxx…

一文带你掌握Spring事务核心:TransactionDefinition详解!

TransactionDefinition是Spring框架中用于定义事务属性的核心接口。在Spring的事务管理中&#xff0c;这个接口扮演着至关重要的角色&#xff0c;它允许开发者定制事务的各种属性&#xff0c;如隔离级别、传播行为、超时时间以及是否只读。 基本介绍 TransactionDefinition 接…

【抄作业】ImportError :cannot import name xxxxxx ,原博主Activewaste

前情介绍 网上关于这种问题的解决方案一大堆&#xff0c;但是绝大多数都是不适用&#xff0c;或者说解决不了问题&#xff0c;我根据别人所遇到的和我自己遇到的&#xff0c;对这个问题整理了一下&#xff0c;希望能解决这个问题。 问题分析 一、缺少这个module或者func或者p…

代码随想录算法训练营第五十二天 _ 动态规划_300. 最长递增子序列、674.最长连续递增序列、718.最长重复子数组。

学习目标&#xff1a; 动态规划五部曲&#xff1a; ① 确定dp[i]的含义 ② 求递推公式 ③ dp数组如何初始化 ④ 确定遍历顺序 ⑤ 打印递归数组 ---- 调试 引用自代码随想录&#xff01; 60天训练营打卡计划&#xff01; 学习内容&#xff1a; 300. 最长递增子序列 动态规划五…

外汇天眼:投资者最大的敌人——你的大脑

人类大脑的三层构成 为了深入了解投资者做出非理性决策的原因&#xff0c;考虑人脑及其对快乐和痛苦的反应是很有启发性的。 我们的大脑已经进化了数百万年&#xff0c;由三层组成。 核心是我们原始的大脑&#xff0c;它提供了维持我们生存的战斗或逃跑本能。 上面覆盖着一个…

前端如何使用express写一个简单的服务

相信不少前端平常在日常工作中肯遇见过后端API接口没开发出来的时候吧 前端提升小技巧 自己使用nodejs——express ,koa&#xff0c;egg开发接口吧(本人比较喜欢egg和express) 今天先分享一下express 下面是一个简单的demo 1、首先咱们可以新建一个文件夹,创建一个app.js 下…

循环神经网络(1)循环神经网络的记忆能力实验

循环神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;是一类具有短期记忆能力的神经网络&#xff0e;在循环神经网络中&#xff0c;神经元不但可以接受其他神经元的信息&#xff0c;也可以接受自身的信息&#xff0c;形成具有环路的网络结构&#xff…

Github、Gitee优秀的开源项目分享

先赞后看&#xff0c;养成习惯&#xff01;&#xff01;&#xff01;❤️ ❤️ ❤️ 资源收集不易&#xff0c;如果喜欢可以关注我哦&#xff01; ​如果本篇内容对你有所启发&#xff0c;欢迎访问我的个人博客了解更多内容&#xff1a;链接地址 ​ Java 项目 javacore - Java …

ArkTS组件通信

父子通信 情况一&#xff1a;子组件只展示父组件中的状态 父组件通过 State修饰符 定义变量&#xff0c;子组件通过 Prop修饰符 获取变量。 Prop是 「单向传递」&#xff0c;父组件将变量「拷贝」一份交给子组件使用&#xff0c;子组件不可修改变量。 父组件 // 声明变量 …

java写个爬虫抓取汽车之家车型配置参数

前几天有个搞工程的表弟找我&#xff0c;问我什么车好&#xff0c;可以经常跑工地的&#xff0c;看上去又有面子。于是我挥动发财的小手&#xff0c;写一个爬虫程序&#xff0c;筛选并整理了一些数据&#xff0c;并附上下载的图片提供参考&#xff0c;看中了果断第二天提车到手…

C#教程(一):面向对象

1、介绍 C#是一种多范式编程语言&#xff0c;但其中一个主要的编程范式是面向对象编程&#xff08;OOP&#xff09;。面向对象编程有一些特点&#xff0c;而C#提供了丰富的功能来支持这些特点。 2、面向对象特点 封装&#xff08;Encapsulation&#xff09;&#xff1a; 封装…

华为OD机试-传递悄悄话(JavaPythonGo)100%通过率

题意 给定一个二叉树,每个节点上站着一个人,节点数字表示父节点到该节点传递悄悄话需要花费的时间。初始时,根节点所在位置的人有一个悄悄话想要传递给其他人,求二又树所有节点上的人都接收到悄悄话花费的时间。 输入 给定一叉树 09 20-1-1 157-1-1-1-132 注:-1表示空节…

Unity | AVpro的最基础使用方法(视频播放插件)

一、 AVpro的使用方法 (一)准备播放器MediaPlayer 1. AVpro的播放器是MediaPlayer&#xff0c;在Heirarchy面板里创建 2.播放器里放视频 a.把视频放到StreamingAssets文件夹下 b.你就可以在MediaPlayer里面找到这个视频 c.选中以后&#xff0c;就会变成 这里点击播放可以播放…

WEX ISO 8583通信协议

1、什么是ISO 8583 ISO 8583是国际标准化组织&#xff08;ISO&#xff09;定义的一种金融交易协议&#xff0c; 它定义了一种消息格式&#xff0c;用于在不同的金融系统之间传递交易请求和响应2、Java如何实现ISO 8583 1、引入依赖包<dependency><groupId>org.jp…

spring boot 实现直播聊天室(二)

spring boot 实现直播聊天室(二) 技术方案: spring bootnettyrabbitmq 目录结构 引入依赖 <dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.96.Final</version> </dependency>Si…

使用YOLOv8训练图集详细教程

准备自己的数据集 训练YOLOv8时&#xff0c;选择的数据格式是VOC&#xff0c;因此下面将介绍如何将自己的数据集转换成可以直接让YOLOv8进行使用。 1、创建数据集 我的数据集都在保存在mydata文件夹&#xff08;名字可以自定义&#xff09;&#xff0c;目录结构如下&#xf…

万界星空科技MES---制造企业的加工生产模式

在现代制造业中&#xff0c;加工生产模式是制造企业组织和管理生产过程的重要方面。不同的加工模式适用于不同的生产需求和产品类型。其中流水型、离散型和混合型是三种常见的加工生产模式。1. 流水型加工模式 流水型加工模式是一种高度自动化的生产方式&#xff0c;适用于…

羊大师解答,鲜羊奶应该怎样煮才好喝?

羊大师解答&#xff0c;鲜羊奶应该怎样煮才好喝&#xff1f; 你是否对如何煮鲜羊奶感到困惑&#xff1f;继续阅读本文&#xff0c;小编羊大师将为大家揭秘鲜羊奶的烹饪方法。不管是作为配料还是单独享用&#xff0c;了解如何煮鲜羊奶将会让您获得更加美味又营养丰富的食物。接…

mysql8 windows下修改my.ini配置 this is incompatible with sql_mode=only_full_group_by

1、找到安装路径 show variables like %sql_mode;SHOW VARIABLES LIKE config_file;SHOW VARIABLES LIKE %datadir%;SHOW VARIABLES; 2、修改 sql_modeSTRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION