文章目录
七、练习
1、准备工作
将写好的登录页面login.html放入静态资源文件夹内,准备作为首页,并且设置一个原首页。
2、设置访问首页
首先设置一个空页面index.html作为首页,通过它去转到登录界面
设置页面转换:
@RequestMapping({"/","/index.html"})
public String index(){
return "index";
}
注意
在这里不加 @ResponseBody的原因是,这个注解的作用是把return的数据以json数据类型返回到HTTP response body中(一般用于异步交互),而不会被解析为页面跳转行为,如果加上了此注解,则会直接显示login这五个char,因此在这里不能添加。
但是如果我们进行login页面访问的时候都需要刷新一遍数据,未免有点太麻烦了,所以可以addViewController添加视图映射,启动的时候自动就映射到login页面。
因此不要忘记将刚才写的代码注释掉
@Bean //将组件注册在容器
public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("login");
registry.addViewController("/index.html").setViewName("login");
registry.addViewController("/main.html").setViewName("dashboard");
}
};
return adapter;
}
在这里需要修改一下资源引用,一般SpringBoot都是默认从静态资源文件夹获取到的,在这里我们在引用一下webjars,用th:href的好处就是:如果有一天项目的访问名改变,比如我们是在application.properties中加了个server.context-path=/xxx,就是说,给这个域名加了个前缀,如果我们不用th:href,不会帮我们加上的,但是使用之后,就会默认把/xxx加上,防止我们出现404的问题
<!--Bootstrap core CSS-->
<li href="asserts/css/bootstrap.min.css" th:href="@{/}" rel="stylesheet"></li>
填入th:href的具体方法是:找到前面link href的路径在webjars的目录下,添加进来
<!-- Bootstrap core CSS -->
<link href="asserts/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/4.0.0/css/bootstrap.css}" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="asserts/css/dashboard.css" th:href="@{/asserts/css/dashboard.css}" rel="stylesheet">
3、国际化
在resources目录下创建国际化配置文件
切换到Resource Bundle
在properties配置文件中配置上
# 国际化配置文件(包名.基础名)
spring.messages.basename=i18n.login
编译之后SpringBoot会自动配置好管理国际化资源文件的组件
让页面获取到国际化的值
<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
<label class="sr-only" th:text="#{login.username}">Username</label>
<input type="text" class="form-control" placeholder="Username" th:placeholder="#{login.username}" required="" autofocus="">
<label class="sr-only" th:text="#{login.password}">Password</label>
<input type="password" class="form-control" placeholder="Password" th:placeholder="#{login.password}" required="">
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"/> [[#{login.remember}]]
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>
运行之后会出现乱码,我们需要做如下配置修改
运行的效果如下:
通过一个超链接去转换国际化的选项,放在任意的位置即可
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
运行首页,点击超链接,地址名会改变成对应区域的链接,因为还使用了thymeleaf,所以不需要用问号代替参数,直接用小括号,小括号内的值就是value,但是这时我们还无法做到通过链接来达到国际化,所以需要写一个区域信息解析器
/**
* 可以在连接上携带区域信息
*/
public class MyLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
String l = request.getParameter("l");
Locale locale = Locale.getDefault();
if(!StringUtils.isEmpty(l)){
String[] split = l.split("_");
locale = new Locale(split[0],split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
写完区域信息解析器之后发现,并没有生效,因为在LocaleResover底层源代码中,发现有一个@ConditionalOnMissingBean,意思是在我们没有区域信息解析器的时候才会去添加,而底层代码中,有一个AcceptHeaderLocaleResolver会被默认使用到,而且也在自动配置中,所以会默认直接使用这个区域信息解析器,而无法使用到我们手写的。
所以需要在配置类中将组件手动添加进去
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
运行,国际化界面就可以完成了。
登录界面代码:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Signin Template for Bootstrap</title>
<!-- Bootstrap core CSS -->
<link href="asserts/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/4.0.0/css/bootstrap.css}" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="asserts/css/signin.css" th:href="@{/asserts/css/signin.css}" rel="stylesheet">
</head>
<body class="text-center">
<form class="form-signin" action="dashboard.html">
<img class="mb-4" th:src="@{/asserts/img/bootstrap-solid.svg}" src="asserts/img/bootstrap-solid.svg" alt="" width="72" height="72">
<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
<label class="sr-only" th:text="#{login.username}">Username</label>
<input type="text" class="form-control" placeholder="Username" th:placeholder="#{login.username}" required="" autofocus="">
<label class="sr-only" th:text="#{login.password}">Password</label>
<input type="password" class="form-control" placeholder="Password" th:placeholder="#{login.password}" required="">
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"/> [[#{login.remember}]]
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>
<p class="mt-5 mb-3 text-muted">© 2017-2018</p>
<a class="btn btn-sm">中文</a>
<a class="btn btn-sm">English</a>
</form>
</body>
</html>
4、登录
开发期间模板引擎页面修改以后,要实时生效,所以需要修改如下配置:
1、禁用模板引擎的缓存
# 禁用缓存
spring.thymeleaf.cache=false
2、页面修改完成之后ctrl+F9:重新编译
在login.html中使用thymeleaf模拟一个post的登录请求
对login请求写一个登录验证方法
@Controller
public class LoginController {
// @DeleteMapping
// @PutMapping
// @GetMapping
//@RequestMapping(value = "/user/login",method = RequestMethod.POST)
@PostMapping(value = "/user/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password,
Map<String,Object> map, HttpSession session){
if(!StringUtils.isEmpty(username) && "123456".equals(password)){
//登陆成功,防止表单重复提交,可以重定向到主页
session.setAttribute("loginUser",username);
return "redirect:/main.html";
}else{
//登陆失败
map.put("msg","用户名密码错误");
return "login";
}
}
}
不能让表单重复提交,可以重定向到一个main页面,使用redirect:/main.html然后把页面通过视图映射,映射到我们刚刚的dashboard即可。
@Bean //将组件注册在容器
public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("login");
registry.addViewController("/index.html").setViewName("login");
registry.addViewController("/main.html").setViewName("dashboard");
}
};
return adapter;
}
@Controller
public class LoginController {
// @DeleteMapping
// @PutMapping
// @GetMapping
//@RequestMapping(value = "/user/login",method = RequestMethod.POST)
@PostMapping(value = "/user/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password,
Map<String,Object> map, HttpSession session){
if(!StringUtils.isEmpty(username) && "123456".equals(password)){
//登陆成功,防止表单重复提交,可以重定向到主页
session.setAttribute("loginUser",username);
return "redirect:/main.html";
}else{
//登陆失败
map.put("msg","用户名密码错误");
return "login";
}
}
}
登录,发现无法识别username,这是因为没有在login内指明我们传给map的key值,也就是username,所以要在下面声明一下username和password
如果我们password输入错误了,也需要给予server端一个提示说输入错误了,所以要在login中,在Please Sign in这一行加上
<!--判断计算,当if成立,其他标签才能生效,原理是标签的优先级-->
<p style="color:red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
可以登录到dashboard页面,但是有一个问题,我们可以通过地址跨过验证界面,跳到登录后的界面,这时我们需要设置一个拦截器,把登录过的username放进一个HTTPSession的对象中存起来,从而其他的用户会被拦截。
书写拦截器
/**
* 登陆检查,没有登录的用户无法进入登录后的页面
*/
public class LoginHandlerInterceptor implements HandlerInterceptor {
//目标方法执行之前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object user = request.getSession().getAttribute("loginUser");
if(user == null){
//未登陆,返回登陆页面
request.setAttribute("msg","没有权限请先登陆");
request.getRequestDispatcher("/index.html").forward(request,response);
return false;
}else{
//已登陆,放行请求
return true;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
在WebMvcConfigurerAdapter注册拦截器
//注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
//super.addInterceptors(registry);
//静态资源; *.css , *.js
//SpringBoot已经做好了静态资源映射
registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
.excludePathPatterns("/index.html","/","/user/login");
}
注意:一定要写上/,不然会报405
dashboard.html代码
<!DOCTYPE html>
<!-- saved from url=(0052)http://getbootstrap.com/docs/4.0/examples/dashboard/ -->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Dashboard Template for Bootstrap</title>
<!-- Bootstrap core CSS -->
<link href="asserts/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/4.0.0/css/bootstrap.css}" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="asserts/css/dashboard.css" th:href="@{/asserts/css/dashboard.css}" rel="stylesheet">
<style type="text/css">
/* Chart.js */
@-webkit-keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
@keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
.chartjs-render-monitor {
-webkit-animation: chartjs-render-animation 0.001s;
animation: chartjs-render-animation 0.001s;
}
</style>
</head>
<body>
<!--引入topbar-->
<div th:replace="commons/bar::topbar"></div>
<div class="container-fluid">
<div class="row">
<!--引入sidebar-->
<div th:replace="commons/bar::#sidebar(activeUri='main.html')"></div>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<div class="chartjs-size-monitor" style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: hidden; pointer-events: none; visibility: hidden; z-index: -1;">
<div class="chartjs-size-monitor-expand" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;">
<div style="position:absolute;width:1000000px;height:1000000px;left:0;top:0"></div>
</div>
<div class="chartjs-size-monitor-shrink" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;">
<div style="position:absolute;width:200%;height:200%;left:0; top:0"></div>
</div>
</div>
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
<h1 class="h2">Dashboard</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group mr-2">
<button class="btn btn-sm btn-outline-secondary">Share</button>
<button class="btn btn-sm btn-outline-secondary">Export</button>
</div>
<button class="btn btn-sm btn-outline-secondary dropdown-toggle">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-calendar"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg>
This week
</button>
</div>
</div>
<canvas class="my-4 chartjs-render-monitor" id="myChart" width="1076" height="454" style="display: block; width: 1076px; height: 454px;"></canvas>
</main>
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script type="text/javascript" src="asserts/js/jquery-3.2.1.slim.min.js" th:src="@{/webjars/jquery/3.3.1/jquery.js}"></script>
<script type="text/javascript" src="asserts/js/popper.min.js" th:src="@{/webjars/popper.js/1.11.1/dist/popper.js}"></script>
<script type="text/javascript" src="asserts/js/bootstrap.min.js" th:src="@{/webjars/bootstrap/4.0.0/js/bootstrap.js}"></script>
<!-- Icons -->
<script type="text/javascript" src="asserts/js/feather.min.js" th:src="@{/asserts/js/feather.min.js}"></script>
<script>
feather.replace()
</script>
<!-- Graphs -->
<script type="text/javascript" src="asserts/js/Chart.min.js" th:src="@{/asserts/js/Chart.min.js}"></script>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
datasets: [{
data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
lineTension: 0,
backgroundColor: 'transparent',
borderColor: '#007bff',
borderWidth: 4,
pointBackgroundColor: '#007bff'
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: false
}
}]
},
legend: {
display: false,
}
}
});
</script>
</body>
</html>
5、CRUD-员工列表
实验需求:
<1>、RestfulCRUD
URI: /资源名称/资源标识 HTTP请求方式区分对资源CRUD操作
普通CRUD(uri来区分操作) | RestfulCRUD | |
---|---|---|
查询 | getEmp | emp—GET |
添加 | addEmp?xxx | emp—POST |
修改 | updateEmp?id=xxx&xxx=xx | emp/{id}—PUT |
删除 | deleteEmp?id=1 | emp/{id}—DELETE |
<2>、实验请求架构
实验功能 | 请求URI | 请求方式 |
---|---|---|
查询所有员工 | emps | GET |
查询某个员工(来到修改页面) | emp/1 | GET |
来到添加页面 | emp | GET |
添加员工 | emp | POST |
来到修改页面(查出员工进行信息回显) | emp/1 | GET |
修改员工 | emp | PUT |
删除员工 | emp/1 | DELETE |
实现方法
<1>、员工列表
把要跳转的list页面在dashboard.html中找到位置,然后用thymeleaf模板引擎定义RequestMapping属性,方便后面设置请求能够顺利到达页面
编写一个映射方法通过点击Customer到达list.html的方法
return的时候不需要写上templates就是因为在ThymeleafProperties配置中已经为我们默认配置了这个路径头。
@Controller
public class EmployeeController {
@Autowired
EmployeeDao employeeDao;
//查询所有员工返回列表页面
@GetMapping("/emps")
public String list(Model model){
Collection<Employee> employees = employeeDao.getAll();
//放在请求域中
model.addAttribute("emps",employees);
// thymeleaf默认就会拼串
// classpath:/templates/xxxx.html
return "emp/list";
}
}
<2>、thymeleaf公共页面元素抽取
(1)、抽取片段
抽取公共片段
<div th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</div>
(2)、引入片段
引入公共片段
<div th:insert="~{footer :: copy}"></div>
~{templatename::selector}:模板名::选择器
~{templatename::fragmentname}:模板名::片段名
(3)、实验(1、2)
把dashboard的顶部栏,给list页面使用
dashboard的顶部栏
list的顶部栏
现在要实现,使用上面介绍的公共元素抽取和引用的方法来实现统一,同时节省写代码的时间,首先需要抽取元素:
<!--topbar-->
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">[[${session.loginUser}]]</a>
<input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Sign out</a>
</li>
</ul>
</nav>
在dashboard找到顶部栏的源代码,加上了fragment属性和topbar属性值
再去到list找到顶部栏进行替换
<!--引入抽取的topbar-->
<!--模板名:会使用thymeleaf的前后缀配置规则进行解析-->
<div th:replace="commons/bar::topbar"></div>
注:
1.replace的功能片段在div标签中
2.引入功能的标签除了replace还有两种
th:insert:将公共片段整个插入到声明引入的元素中
th:replace:将声明引入的元素替换为公共片段
th:include:将被引入的片段的内容包含进这个标签中
(4)、参数化的片段签名
现在发现,虽然已经成功引入公共模板,但是并没有高亮的效果,然后我们来设置页面高亮,可以发现,只需要在对应的属性标签上使用active属性,就可以实现选项高亮,但是我们只有在真正点入页面的时候,大部分的选项卡应该是只有鼠标点上去才会变成高亮,所以我们现在将要实现这个功能
我们在1、2中声明th:fragment中都是只标记模板名,现在可以填入两个参数,那么在这个片段中就可以使用 这两个参数,然后在模板引用的时候也可以通过设置的两个参数进行导入。但是在声明fragment的时候也可以不生命具体的参数,而引入的时候也可以直接声明+引用
接下来我们在templates文件夹下创建一个commons目录,用来存放公共模板,并且新建一个html,在里面注入公共模板的代码
<div th:replace="~{commons/bar :: topbar}"></div>
<div th:replace="~{commons/bar :: #sidebar}"></div>
然后将模板引入代码对应的填到dashboard和list对应位置中
重新进行测试,发现没有问题
现在要实现进入到Employee Manage界面之后可以通过点击dashboard能够返回到原来的emps界面,我们来到bar进行thymeleaf模板引擎的引入
<a class="nav-link active" href="#" th:href="@{/main.html}">
实现选项卡高亮功能,只有鼠标在选中的时候才会高亮,原理就是,因为高亮是设置到a标签的class属性上,所以只需要用th:class引入判断即可
首先设置list
<!--引入侧边栏-->
<div th:replace="commons/bar::#sidebar(activeUri='emps')"></div>
再设置dashboard
<!--引入sidebar-->
<div th:replace="commons/bar::#sidebar(activeUri='main.html')"></div>
重新进行a标签的书写,还没有指明activeUrl是干什么的
来到bar页面
<a class="nav-link active"
th:class="${activeUri=='main.html'?'nav-link active':'nav-link'}"
href="#" th:href="@{/main.html}">
<a class="nav-link active" href="#" th:href="@{/emps}"
th:class="${activeUri=='emps'?'nav-link active':'nav-link'}">
<3>、遍历取出员工数据
来到list页面
删除原来的tbody标签,自己手写一个tbody标签
目的是通过tbody里面放的一个遍历,达到输出所有员工数据功能。
<thead>
<tr>
<th>#</th>
<th>lastName</th>
<th>email</th>
<th>gender</th>
<th>department</th>
<th>birth</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr th:each="emp:${emps}">
<td th:text="${emp.id}"></td>
<td>[[${emp.lastName}]]</td>
<td th:text="${emp.email}"></td>
<td th:text="${emp.gender}==0?'女':'男'"></td>
<td th:text="${emp.department.departmentName}"></td>
<td th:text="${emp.birth}"></td>
</tr>
</tbody>
这时我们发现生日日期的格式和我们平时的格式不太一样,因此需要进行日期格式化
<td th:text="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}"></td>
<4>、CRUD操作
(1)、添加
在td标签内添加按钮来实现对员工列表的crud操作。首先添加按钮对应刚才添加的每一行的员工信息,添加到循环体中
<td>
<button class="btn btn-sm btn-primary">edit</button>
<button class="btn btn-sm btn-danger">delete</button>
</td>
需要通过点击员工添加按钮来到一个新的页面完成业务的实现,所以要把员工添加变成一个button,因为是a标签,所以发送get请求,来到emp界面
<h2 a class="btn btn-sm btn-success" href="emp" th:href="@{/emp}">员工添加</a></h2>
添加的表格模板来自于
<form>
<div class="form-group">
<label>LastName</label>
<input type="text" class="form-control" placeholder="zhangsan">
</div>
<div class="form-group">
<label>Email</label>
<input type="email" class="form-control" placeholder="zhangsan@atguigu.com">
</div>
<div class="form-group">
<label>Gender</label><br/>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="gender" value="1">
<label class="form-check-label">男</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="gender" value="0">
<label class="form-check-label">女</label>
</div>
</div>
<div class="form-group">
<label>department</label>
<select class="form-control">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</div>
<div class="form-group">
<label>Birth</label>
<input type="text" class="form-control" placeholder="zhangsan">
</div>
<button type="submit" class="btn btn-primary">添加</button>
</form>
增加一个新的功能:点完员工添加之后能够通过显示部门然后再添加员工
首先要把department的信息以集合的方式用get请求传到add界面内,让add界面能够顺利使用
//来到员工添加页面
@GetMapping("/emp")
public String toAddPage(Model model){
//来到添加页面,查出所有的部门,在页面显示
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("depts",departments);
return "emp/add";
}
显示出部门的信息,要使用到option属性,并且直接遍历每一个部门的Name
<!--提交的是部门的id-->
<select class="form-control" name="department.id">
<option th:selected="${emp!=null}?${dept.id == emp.department.id}" th:value="${dept.id}" th:each="dept:${depts}" th:text="${dept.departmentName}">1</option>
</select>
用thymeleaf模板去遍历department信息,然后用th:text显示departmentName信息,供添加的时候去选择。
然后点击页面下面的添加,进行添加请求,要用到action属性,以post请求去请求到。
//员工添加
//SpringMVC自动将请求参数和入参对象的属性进行一一绑定;要求请求参数的名字和javaBean入参的对象里面的属性名是一样的
@PostMapping("/emp")
public String addEmp(Employee employee){
//来到员工列表页面
System.out.println("保存的员工信息:"+employee);
// redirect: 表示重定向到一个地址 /代表当前项目路径
// forward: 表示转发到一个地址
return "redirect:/emps";
}
然后回到add页面找到表单去进行提交属性的定义
然后回到EmployeeController.java进行保存
//员工添加
//SpringMVC自动将请求参数和入参对象的属性进行一一绑定;要求请求参数的名字和javaBean入参的对象里面的属性名是一样的
@PostMapping("/emp")
public String addEmp(Employee employee){
//来到员工列表页面
System.out.println("保存的员工信息:"+employee);
//保存员工
employeeDao.save(employee);
// redirect: 表示重定向到一个地址 /代表当前项目路径
// forward: 表示转发到一个地址
return "redirect:/emps";
}
注意:日期格式一定要对,不然会出现405的错误
(2)、修改
<a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.id}">编辑</a>
id在遍历的过程中是不断变化的,所以无法直接在进行请求的时候直接标注,只能用拼接的方法进行请求。请求路径必然是/emp,后面带的变量id就要使用{id}进行请求了,然后我们来写页面跳转的代码:
//来到修改页面,查出当前员工,在页面回显
@GetMapping("/emp/{id}")
public String toEditPage(@PathVariable("id") Integer id,Model model){
Employee employee = employeeDao.get(id);
model.addAttribute("emp",employee);
//页面要显示所有的部门列表
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("depts",departments);
//回到修改页面(add是一个修改添加二合一的页面);
return "emp/add";
}
为了能够方便修改,所以在add界面要修改的对应地方加上th:value表示选中内容即可,比如:th:value="${emp.id}"
在遍历阶段,要写成
<option th:selected="${dept.id == emp.department.id}" th:value="${dept.id}" th:each="dept:${depts}" th:text="${dept.departmentName}">1</option>
在实现添加和修改页面二合一的时候需要使用:
<input class="form-check-input" type="radio" name="gender" value="1" th:checked="${emp!=null}?${emp.gender==1}">
修改之后重新测试Edit按钮发现成功,但是发现添加又出了问题,因为这是二合一页面,所以接下来要这样:在刚才所有添加的标签上添加
${emp!=null}
接下来要修改发送请求,因为只有在edit的时候才会修改,因此要加上一个判断
<input type="hidden" name="_method" value="put" th:if="${emp!=null}"/>
编写put请求修改后的跳转页面,因为默认不支持put请求,只支持get和post
//员工修改;需要提交员工id;
@PutMapping("/emp")
public String updateEmployee(Employee employee){
System.out.println("修改的员工数据:"+employee);
employeeDao.save(employee);
return "redirect:/emps";
}
观察到控制台的id=null,因为没有把无法修改的id值进行返回,所以要默认进行返回
<input type="hidden" name="id" th:if="${emp!=null}" th:value="${emp.id}">
(3)、删除
以字符串拼接和delete请求去发送
<tbody>
<tr th:each="emp:${emps}">
<td>
<button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteBtn">删除</button>
</td>
</tr>
</tbody>
<form id="deleteEmpForm" method="post">
<input type="hidden" name="_method" value="delete"/>
</form>
还需要在list.html中编写删除的script
<script>
$(".deleteBtn").click(function(){
//删除当前员工的
$("#deleteEmpForm").attr("action",$(this).attr("del_uri")).submit();
return false;
});
</script>
编写员工删除的java代码
//员工删除
@DeleteMapping("/emp/{id}")
public String deleteEmployee(@PathVariable("id") Integer id){
employeeDao.delete(id);
return "redirect:/emps";
}
运行效果图:
观察到按钮变成form表单,并且测试成功。
到此RestfulCRUD练习就完成了!!!