文章目录
1.导入静态资源:
结构如图所示:
html文件放在templates文件夹下(不可直接访问,只能通过controller跳转访问),其他放在static文件夹下。
注:注意修改html页面中的链接地址将其改为thymeleaf格式(要记得加thymeleaf的依赖和头文件)
如:
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="" width="72" height="72">
URL表达式@{…}
使用@{…}括起来,并且其中只能写一个绝对URL或相对URL地址的表达式,称为URL表达式。这个绝对/相对URL地址中一般是包含有动态参数的,需要结合变量表达式${…}进行字符串拼接。
在URL表达式中,Thymeleaf会将开头的斜杠(/)解析为当前工程的上下文路径ContextPath,而浏览器会自动为其添加“http://主机名:端口号”,即其即为一个绝对路径
2.用户登录:
效果:一输入url就跳转到登录的index页面,表单登录,在后台对输入的数据进行判断,若正确跳转到用户表页面,若错误跳转登录页面并输出提示信息。并且用户不可以在没有登录的情况下直接访问用户表页面,会出现信息提示。
2.1 首页设置
2.1.1 首页跳转
因为templates文件夹下的页面不可以直接访问,所以我们要先定义一个视图跳转。
方法一:
在controller中编写(不推荐)
@RequestMapping("/")
public String index(){
return "index";
}
方法二:
自定义mvc配置:在congif文件夹下创建myMvcConfig,这样不管我们是直接打开还是输入index.html都可以直接到登录页面。(推荐)
@Configuration
public class myMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/main.html").setViewName("dashboard");
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
}
注:在这个项目中不管是否设置,打开项目都是直接到登录页面的。因为在前一章的笔记我们可以看到源码中设置,springboot会自动的将templates文件夹下的index.html设置为首页。
还需要注意config文件夹的位置要不然配置不生效!
2.1.2 登录验证
将表单提交到 /user/index
<form class="form-signin" th:action="@{/user/index}">
<input type="text" name="username" class="form-control" placeholder="Username" required="" autofocus="">
<input type="password" name="pwd" class="form-control" placeholder="Password" required="">
如果输入有错误就显示错误提示,这里使用了thymeleaf的语法。
th:if 该属性用于逻辑判断
#strings:关于字符串的thymeleaf的内置对象,如果msg为空,则显示提示信息。
<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"/>
public class indexController {
@RequestMapping("/user/index")
public String index(@RequestParam("username") String username, @RequestParam("pwd")String pwd, Model model, HttpSession session){
if(username.equals("www") && "1234".equals(pwd)){
session.setAttribute("loginName",username);
return "redirect:/main.html";
}else {
model.addAttribute("msg","输入错误");
return "index";
}
}
}
在这里我们会发现登录成功后的url显示的非常的。。简陋
所以我们设置了一个“假”的页面main.html,将dashboard.html映射到此页面上
return “redirect:/main.html”;
2.2 拦截器
思路:设置拦截器,在登录页面将值传入session表示登录成功,通过session是否为空来判断是否需要拦截。
- 在congfig目录下创建myLoginInterceptor
如果session中没有值,就输出提示信息然后转发回去。
public class myLoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object username = request.getSession().getAttribute("loginName");
if (username == null){
request.setAttribute("msg","没有权限");
request.getRequestDispatcher("/index.html").forward(request,response);
return false;
}else {
return true;
}
}
}
- 在myMvcConfig中添加拦截的路径
excludePathPatterns():添加不被拦截的资源(静态资源和登录页面)
注:/** 表示所有文件包括文件夹,/* 表示所有文件。
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new myLoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/index.html","/","static/**","/user/index");
}
另:我们还可以利用session中的值在用户页面显示用户名,这里的[[${session.loginName}]]等同于
th:text="${msg}"
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">[[${session.loginName}]]</a>
3.员工列表展示:
3.1 代码复用实现:
我们发现首页和员工列表页面的顶部导航栏和侧边栏都是相同的,我们用thymeleaf的th:fragment:语法达到代码复用。这样我们只需要修改一处的代码所有的样式就都修改了。
-
将要复用的代码放在单独的类中:commons下的commen,分别设置他们的名称为topbar和sidebar。
注:在html中,nav标签是html5版本中新增的标签,是使用来定义导航链接的部分。nav标签只是用来表示该区域是导航链接,默认并没有什么显示效果。
th:fragment:定义代码块,方便被th:insert引用。
-
在页面对应的地方引用:
~{…}: 代码块表达式
推荐写法:~{templatename::fragmentname}
支持写法:~{templatename::#id}
templatename:模版名,Thymeleaf会根据模版名解析完整路径:/resources/templates/templatename.html,要注意文件的路径。
fragmentname:片段名,Thymeleaf通过th:fragment声明定义代码块,即:th:fragment=“fragmentname”
id:HTML的id选择器,使用时要在前面加上#号,不支持class选择器。支持:~{templatename::#id}
代码块表达式需要配合th属性(th:insert,th:replace,th:include)一起使用。
th:insert:将代码块片段整个插入到使用了th:insert的HTML标签中,
th:replace:将代码块片段整个替换使用了th:replace的HTML标签中,
th:include:将代码块片段包含的内容插入到使用了th:include的HTML标签中,
<nav th:replace="~{commons/common::topbar}"></nav>
<nav th:replace="~{commons/common::sidebar}"></nav>
3.2 controller层编写:
编写跳转到员工列表页面的程序。
需要在employeeDao中添加@Repository注解,然后注意要写全类名,要不然会报错。
修改导航栏代码中的对应路径。
@Controller
public class employeeController {
@Autowired
com.example.demo.dao.employeeDao employeeDao;
@RequestMapping("/employee.html")
public String getEmployee(Model model){
Collection employee = employeeDao.getEmployee();
model.addAttribute("emps",employee);
return "employ/list";
}
}
将组件通过注解的方法加入到spring容器中:
controller层:@Controller
service层:@Service
Dao层:@Repository
通过@Autowired对属性进行标记,完成自动装配工作。
3.3 设置侧边栏点击高亮
高亮原理:
nav-link active //表示高亮
nav-link //表示常规
在每个页面侧边栏的复用代码上加上一个传入active的值:
<nav th:replace="~{commons/common::sidebar(active='main')}"></nav>
<nav th:replace="~{commons/common::sidebar (active='list')}"></nav>
然后在侧边栏内的代码中进行判断:
注:thymeleaf中要使用 ’ ’ 而不是 “ ” 。
(可能会报错系统识别不到active但是可以运行成功。)
<a th:class="${active == 'main' ?'nav-link active':'nav-link'}" th:href="@{/main.html}">
<a th:class="${active == 'list'?'nav-link active':'nav-link'}" th:href="@{/employee.html}">
用到了thymeleaf的三目表达式:
Conditional operators:
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
3.4 员工信息显示
th:each属性用于迭代循环,语法:th:each=“obj,iterStat:${objList}”
<tbody>
<tr th:each="emp:${emps}">
<td th:text="${emp.getId()}"> </td>
<td th:text="${emp.getName()}"> </td>
<td th:text="${emp.getAge()}"> </td>
<td th:text="${emp.getGender() == 0 ?'男' : '女'}"></td>
<td th:text="${emp.getBirth()}"> </td>
<td th:text="${emp.department.getDepartmentName()}"></td>
<td>
<button class="btn btn-sm btn-primary">编辑</button>
<button class="btn btn-sm btn-danger">删除</button>
</td>
</tr>
</tbody>
可以用#dates.format() 用来格式化日期时间
${#dates.format(date, ‘dd/MMM/yyyy HH:mm’)}
${#dates.arrayFormat(datesArray, ‘dd/MMM/yyyy HH:mm’)}
${#dates.listFormat(datesList, ‘dd/MMM/yyyy HH:mm’)}
${#dates.setFormat(datesSet, ‘dd/MMM/yyyy HH:mm’)}
4.员工信息添加:
4.1 编写添加页面:
在bootstrap网站上找到需要的表单样式,运用到页面中。
注:
- main标签不可缺少,元素中的内容对于文档来说应当是唯一的。它不应包含在文档中重复出现的内容,比如侧栏、导航栏、版权信息、站点标志或搜索表单。这里的main标签内包括了表单主体的样式,如果不写main标签表单是显示不出来的,会被侧边栏覆盖掉。
- label 标签的 for 属性:for 属性规定 label 与哪个表单元素绑定(与id绑定)。
- input标签的value属性是这个表单提交上去的真实的值。
- 因为我们的是通过传入部门id查找部门名称来添加部门的,下拉列表也是通过循环来进行值的显示。
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<form th:action="@{/addemp}" method="post">
<div class="form-group">
<label for="name">姓名</label>
<input type="text" class="form-control" id="name" name="name">
</div>
<div class="form-group">
<label for="age">年龄</label>
<input type="text" class="form-control" id="age" name="age">
</div>
<!-- 单选框-->
<label class="radio-inline">
<input type="radio" name="gender" id="inlineRadio1" value="0">
男
</label>
<label class="radio-inline">
<input type="radio" name="gender" id="inlineRadio2" value="1">
女
</label>
<!-- 输入框-->
<div class="form-group">
<label for="birth">生日</label>
<input type="text" class="form-control" id="birth" name="birth">
</div>
<div class="form-group">
<label>部门名称</label>
<select class="form-control" name="department.id">
<option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>
</select>
</div>
<button type="submit" class="btn btn-default">提交</button>
</form>
</main>
4.2 页面完善:
在list页面添加对应的按钮:在后台获得部门名称的集合。
<a th:href="@{/addemp}" class="btn btn-sm btn-success" style="float: right">添加</a>
@GetMapping("/addemp")
public String addEmp(Model model){
//显示部门
Collection departments = departmentDao.getDepartment();
model.addAttribute("departments",departments);
return "employ/add";
}
在form表单中添加提交方法指定为post提交,通过PostMapping来通过不同的提交方法跳转不同的页面,如果不设置提交方式会报错。
@PostMapping("/addemp")
public String addEmp(Employee employee){
employeeDao.add(employee);
return "redirect:/employee";
}
5. 修改和删除员工信息:
1.编写按钮路径:
通过将id传入到后台进行操作
<a class="btn btn-sm btn-primary" th:href="@{/update/}+${emp.getId()}">编辑</a>
<a class="btn btn-sm btn-danger" th:href="@{/delete/}+${emp.getId()}">删除</a>
对应的controller:
@GetMapping("/update/{id}")
public String updateEmp(@PathVariable("id") Integer id,Model model){
Employee employeeById = employeeDao.getEmployeeById(id);
model.addAttribute("updateEmploy",employeeById);
//显示部门
Collection departments = departmentDao.getDepartment();
model.addAttribute("departments",departments);
return "employ/update";
}
@PostMapping("/update")
public String updateEmp2(Employee employee){
employeeDao.add(employee);
return "redirect:/employee";
}
删除对应的controller:
//删除页面
@RequestMapping("/delete/{id}")
public String delete(@PathVariable("id")Integer id){
employeeDao.deleteById(id);
return "redirect:/employee";
}
2.页面编辑:
添加一个隐藏域:
<input type="hidden" name="id" th:value="${updateEmploy.getId()}">
设置表单的默认值:
<input type="text" class="form-control" id="name" name="name" th:value="${updateEmploy.getName()}">
//设置单选框的默认值
<input type="radio" th:checked="${updateEmploy.getGender()==0}" name="gender" id="inlineRadio1" value="0">
//设置下拉框的显示
<select class="form-control" name="department.id">
<option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}" th:selected="${department.getId()==updateEmploy.getDepartment().getId()}"></option>
</select>
3.注:
将404页面放在error文件夹下,idea会默认识别404.