總目標:開發一個spingboot的web項目,項目地址:點我
今天的主要任務是完成員工列表的增刪改功能。
1. 增加
-
在list頁面,對增加按鈕添加映射路徑
<a class="btn btn-sm btn-success" th:href="@{/emp}">員工添加</a>
-
在templates/emp文件夾下添加add.html
add頁面與list頁面相比,topbar和sidebar不變,只不過主體內容多了一個form表單
<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="[email protected]"> </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>
-
EmployeeController添加映射
@Autowired DepartmentDao departmentDao; //來到員工添加頁面 @GetMapping("/emp") public String toAddPage(Model model){ Collection<Department> departments = departmentDao.getDepartments(); model.addAttribute("depts", departments); return "emp/add"; }
-
添加頁面填寫的部門信息,是根據請求域中的depts來填充的,修改add.html
<div class="form‐group"> <label>department</label> <select class="form‐control"> <option th:id="${dept.id}" th:each="dept:${depts}" th:text="${dept.departmentName}">1</option> </select> </div>
-
在添加頁面,修改form表單映射路徑,根據web實戰1中的框架設計,添加員工的請求路徑爲
/emp
,請求方式爲post<form th:action="@{/emp}" method="post">
還要注意form表單中提交的每一個數據都要加上name屬性,屬性值要和實體類Employee對應
-
在EmployeeeController處理請求
//員工添加 //springmvc自動關機將請求參數與入參對象的屬性進行一一綁定, //前提是請求參數的名字與javaBean的屬性值要一致。 @PostMapping("/emp") public String addEmp(Employee employee){ System.out.println("保存的員工信息"+employee); employeeDao.save(employee); //如果返回員工列表用return "emps",將自動拼接爲classpath:/templates/emps.html return "redirect:/emps"; }
-
日期提交格式默認是按照
yyyy/MM/dd
,其他格式就會出現400錯誤。如果想使用yyyy-MM-dd
的格式,需要在application.properties文件中添加spring.mvc.date-format=yyyy-MM-dd
這時就能用
yyyy-MM-dd
而不能使用yyyy/MM/dd
的格式。
2. 修改
-
根據本實驗的請求架構
實驗功能 請求URI 請求方式 來到修改頁面 emp/{id} GET 修改員工 emp PUT 點擊list頁面中的編輯按鈕,會發送
emp/{id}
請求,在list.html中使用連接字符串的方式構成請求路徑。我直接在add.html中做修改,使add.html同時能夠完成添加員工和修改員工的功能。
<a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.id}">編輯</a>
-
在EmployeeController中處理請求
//來到修改頁面,查出當前員工以及部門信息,在頁面回顯
@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.html同時完成添加和修改員工的功能)
return "emp/add";
}
-
修改add.html,爲每個input標籤添加value值,當點擊編輯按鈕的時候,能夠回顯當前員工的值
-
但是經過上一步的修改,add.html頁面的添加功能被擾亂了。修改的時候請求域中有employee和departmeng對象,而添加的時候請求域中只有department對象。當點擊添加按鈕,employee爲空,所以添加頁面很多元素都不顯示。我們可以根據emp是否爲空來判斷當前add.html是在處理修改還是在添加員工
當點擊編輯按鈕,進入編輯頁面,頁面底部的按鈕要根據當前add.html正在完成的功能顯示爲修改
或添加
<button type="submit" class="btn btn-primary" th:text="${emp!=null}?'修改':'添加'"></button>
-
接下來,點擊修改頁面底部的修改按鈕要發送put請求,但是不能直接將表單的請求方法直接改爲
put
。在springmvc中有三步:
1)SpringMVC中配置HiddenHttpMethodFilter,其將請求轉換爲指定的方式 (在springboot已自動配置好)
2)頁面創建一個post表單(當前form即爲post請求)
3)創建一個input項,name="_method";value的屬性值就是我們指定的請求方式
在springboot只需要進行後兩步,我們添加一個隱藏的input標籤,指定value的值爲put,並且當emp不爲空(即當前處理的是修改請求)
<form th:action="@{/emp}" method="post"> <input type="hidden" name="_method" value="put" th:if="${emp!=null}"/>
-
點擊修改按鈕後,上一步已將請求方式改爲put,接下來需要在EmployeeController中添加處理該請求的方法。
@PutMapping("/emp") public String updateEmployee(Employee employee){ System.out.println("修改的員工的數據"+employee); employeeDao.save(employee); return "redirect:/emps"; }
運行程序,修改一個員工信息爲例,發現控制檯輸出的員工信息缺少id,且員工信息沒有修改,而是多加了一個員工,將剛纔的修改信息添加到一個新員工中去了
是因爲調用EmployeeDao中的save()方法時,如果沒有員工id,會自增長id,並添加新員工private static Integer initId = 1006; public void save(Employee employee){ if(employee.getId() == null){ employee.setId(initId++); } employee.setDepartment(departmentDao.getDepartment(employee.getDepartment().getId())); employees.put(employee.getId(), employee); }
因此,當我們修改員工信息時,需要在add.html中提交一個員工id,我們用隱藏的input標籤來完成
<input type="hidden" name="id" th:if="${emp!=null}" th:value="${emp.id}"/>
至此,修改的功能就完成了。
如果不能提交put請求,請查看第4部分的bug處理
3. 刪除
-
根據本實驗的請求架構
實驗功能 請求URI 請求方式 刪除員工 emp/{id} DELETE 點擊list頁面中的刪除按鈕,會發送
emp/{id}
的delete方式的請求,在list.html中使用連接字符串的方式構成請求路徑。我們使用隱藏的input標籤來指定delete請求<form th:action="@{/emp/}+${emp.id}" method="post"> <input type="hidden" name="_method" value="delete"/> <button type="submit" class="btn btn-sm btn-danger">刪除</button> </form>
-
在EmployeeController處理請求
//刪除員工 @DeleteMapping("/emp/{id}") public String deleteEmployee(@PathVariable("id") Integer id){ System.out.println("id:"+id); employeeDao.delete(id); return "redirect:/emps"; }
運行程序,能夠實現刪除員工的功能,但是list頁面中的每一個刪除按鈕都關聯着一個form表單,顯得比較笨重,於是下一步打算把form表單抽取出來,使用js的方式來發送請求。
-
使用js提交請求,這個請求地址應該是從刪除按鈕獲取的。我們給刪除按鈕添加一個自定義的屬性,裏面放入請求路徑,在js中,將這個自定義屬性的值作爲表單form的action值。
刪除按鈕相關代碼爲
<button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteBtn">刪除</button>
將表單放到其他地方
<form id="deleteEmpForm" method="post"> <input type="hidden" name="_method" value="delete"/> </form>
在list.html中添加js代碼
<script>
$(".deleteBtn").click(function(){
//刪除當前員工的
$("#deleteEmpForm").attr("action",$(this).attr("del_uri")).submit();
return false;
});
</script>
然後就大功告成了。
4. bug處理
springboot自動配置了hiddenHttpMethodFilter,這個過濾器幫我們把post請求轉換爲put請求或delete請求。但是我自己動手實踐的過程中,發現這個自動配置並沒有生效。
解決方式有兩種:
-
自己在配置類中添加一個過濾器
//hiddenHttpMethodFilter @Bean public FilterRegistrationBean timeFilter() { FilterRegistrationBean registrationBean = new FilterRegistrationBean(); HiddenHttpMethodFilter myFilter = new HiddenHttpMethodFilter(); registrationBean.setFilter(myFilter); ArrayList<String> urls = new ArrayList<>(); urls.add("/*");//配置過濾規則 registrationBean.setUrlPatterns(urls); return registrationBean; }
-
在application.properties中修改默認配置
查看WebMvcAutoConfig,發現其關於HiddenHttpMethodFilter的代碼如下所示:@Bean @ConditionalOnMissingBean({HiddenHttpMethodFilter.class}) @ConditionalOnProperty( prefix = "spring.mvc.hiddenmethod.filter", name = {"enabled"}, matchIfMissing = false ) public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() { return new OrderedHiddenHttpMethodFilter(); }
註解
@ConditionalOnProperty
的詳細屬性如下所示@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented @Conditional(OnPropertyCondition.class) public @interface ConditionalOnProperty { String[] value() default {}; //數組,獲取對應property名稱的值,與name不可同時使用 String prefix() default "";//property名稱的前綴,可有可無 String[] name() default {};//數組,property完整名稱或部分名稱(可與prefix組合使用,組成完整的property名稱),與value不可同時使用 String havingValue() default "";//可與name組合使用,比較獲取到的屬性值與havingValue給定的值是否相同,相同才加載配置 boolean matchIfMissing() default false;//缺少該property時是否可以加載。如果爲true,沒有該property也會正常加載;反之報錯 boolean relaxedNames() default true;//是否可以鬆散匹配,至今不知道怎麼使用的 } }
matchIfMissing = false
表明在application.properties中如果缺少這個property,不會配置HiddenHttpMethodFilter(不知道我理解的是否正確)。因此組合prifix
和name
,在application.properties中添加如下代碼修改默認配置即可spring.mvc.hiddenmethod.filter.enabled=true