处理 Thymeleaf Spring MVC AJAX 表单及其错误消息的推荐方法
Recommended way to handle Thymeleaf Spring MVC AJAX Forms and their error messages
在 Thymeleaf 方面处理 AJAX 表单及其错误消息的推荐方法是什么?
我目前有一个 Spring 控制器,其中 returns 一个 JSON 字段及其各自错误消息的概述,但不得不求助于使用完全手写的 JQuery(或者只是普通的 Javascript) 感觉有点不对,而且很慢;特别是因为我打算在申请中使用大量表格。
我喜欢做的是在出现错误时替换整个表单。下面是一个超级原始的例子。我不会使用大量片段来呈现表单...只是保持简单。
这是在 Spring 4.2.1 和 Thymeleaf 2.1.4
中编写的
表示用户信息表单的基本 class:UserInfo.java
package myapp.forms;
import org.hibernate.validator.constraints.Email;
import javax.validation.constraints.Size;
import lombok.Data;
@Data
public class UserInfo {
@Email
private String email;
@Size(min = 1, message = "First name cannot be blank")
private String firstName;
}
控制器:UsersAjaxController.java
import myapp.forms.UserInfo;
import myapp.services.UserServices;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.transaction.Transactional;
@Controller
@Transactional
@RequestMapping("/async/users")
public class UsersAjaxController {
@Autowired
private UserServices userServices;
@RequestMapping(value = "/saveUserInfo", method = RequestMethod.POST)
public String saveUserInfo(@Valid @ModelAttribute("userInfo") UserInfo userInfo,
BindingResult result,
Model model) {
// if any errors, re-render the user info edit form
if (result.hasErrors()) {
return "fragments/user :: info-form";
}
// let the service layer handle the saving of the validated form fields
userServices.saveUserInfo(userInfo);
return "fragments/user :: info-success";
}
}
用于呈现表单和成功消息的文件:fragments/user.html
<div th:fragment="info-form" xmlns:th="http://www.thymeleaf.org" th:remove="tag">
<form id="userInfo" name="userInfo" th:action="@{/async/users/saveUserInfo}" th:object="${userInfo}" method="post">
<div th:classappend="${#fields.hasErrors('firstName')}?has-error">
<label class="control-label">First Name</label>
<input th:field="*{firstName}" type="text" />
</div>
<div th:classappend="${#fields.hasErrors('first')}?has-error">
<label class="control-label">Email</label>
<input th:field="*{email}" ftype="text" />
</div>
<input type="submit" value="Save" />
</form>
</div>
<div th:fragment="info-success" xmlns:th="http://www.thymeleaf.org" th:remove="tag">
<p>Form successfully submitted</p>
</div>
JS 代码将简单地将表单提交到表单操作属性中提供的 URL。当响应返回到 JS 回调时,检查是否有任何错误。如果有错误,请将表格替换为响应中的表格。
(function($){
var $form = $('#userInfo');
$form.on('submit', function(e) {
e.preventDefault();
$.ajax({
url: $form.attr('action'),
type: 'post',
data: $form.serialize(),
success: function(response) {
// if the response contains any errors, replace the form
if ($(response).find('.has-error').length) {
$form.replaceWith(response);
} else {
// in this case we can actually replace the form
// with the response as well, unless we want to
// show the success message a different way
}
}
});
})
}(jQuery));
同样,这只是一个基本示例。正如上面评论中提到的,没有正确或错误的方法来解决这个问题。这也不是我的首选解决方案,我肯定会对此进行一些调整,但总的想法是存在的。
注意:我的 JS 代码也有缺陷。如果您用响应中的表单替换表单,表单提交处理程序将不会应用于新替换的表单。如果走这条路,您需要确保在替换表单后正确地重新初始化表单处理程序。
这方面的文档很少,但如果您已经熟悉 Web Flow,则可能不需要更多。我不确定这种技术如何与 Thymeleaf 中的正常 bean 绑定一起使用。我很想看到一个完整的宠物诊所演示应用程序使用它,这样我就可以看到控制器。
不确定这是否可以被视为最佳实践,但这是我所做的:
Map<String, String> errorMap = binding.getFieldErrors()
.stream().collect(Collectors.toMap(
e -> e.getField(), e -> messageSource.getMessage(e, locale)));
然后我将地图发送回 ajax 响应以在 "success" 部分处理。
不知道是否还有人感兴趣,但为了任何正在寻找示例解决方案的人,我将把它留在这里。
我实施了 yorgo 的解决方案并通过在片段中包含脚本来绕过“缺陷”。还有另一个小缺陷,如果有多个带有额外参数的提交按钮(通常用于在 spring + thymeleaf 中实现动态表单),则在序列化表单时信息会丢失。我通过手动将此信息附加到序列化表单来解决这个问题。
您可以在此处查看我对 yorgo 解决方案的实施及其修复:https://github.com/Yepadee/spring-ajax/blob/master/src/main/resources/templates/new_project.html
它的好处在于,您只需执行以下操作即可在任何现有的 thymeleaf 表单上实现它:
- 将脚本放入表单并使其引用表单的 id
- 将表单放入片段
- 将控制器中的 post 方法更改为 return 表单片段而不是整个模板
在 Thymeleaf 方面处理 AJAX 表单及其错误消息的推荐方法是什么?
我目前有一个 Spring 控制器,其中 returns 一个 JSON 字段及其各自错误消息的概述,但不得不求助于使用完全手写的 JQuery(或者只是普通的 Javascript) 感觉有点不对,而且很慢;特别是因为我打算在申请中使用大量表格。
我喜欢做的是在出现错误时替换整个表单。下面是一个超级原始的例子。我不会使用大量片段来呈现表单...只是保持简单。
这是在 Spring 4.2.1 和 Thymeleaf 2.1.4
中编写的表示用户信息表单的基本 class:UserInfo.java
package myapp.forms;
import org.hibernate.validator.constraints.Email;
import javax.validation.constraints.Size;
import lombok.Data;
@Data
public class UserInfo {
@Email
private String email;
@Size(min = 1, message = "First name cannot be blank")
private String firstName;
}
控制器:UsersAjaxController.java
import myapp.forms.UserInfo;
import myapp.services.UserServices;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.transaction.Transactional;
@Controller
@Transactional
@RequestMapping("/async/users")
public class UsersAjaxController {
@Autowired
private UserServices userServices;
@RequestMapping(value = "/saveUserInfo", method = RequestMethod.POST)
public String saveUserInfo(@Valid @ModelAttribute("userInfo") UserInfo userInfo,
BindingResult result,
Model model) {
// if any errors, re-render the user info edit form
if (result.hasErrors()) {
return "fragments/user :: info-form";
}
// let the service layer handle the saving of the validated form fields
userServices.saveUserInfo(userInfo);
return "fragments/user :: info-success";
}
}
用于呈现表单和成功消息的文件:fragments/user.html
<div th:fragment="info-form" xmlns:th="http://www.thymeleaf.org" th:remove="tag">
<form id="userInfo" name="userInfo" th:action="@{/async/users/saveUserInfo}" th:object="${userInfo}" method="post">
<div th:classappend="${#fields.hasErrors('firstName')}?has-error">
<label class="control-label">First Name</label>
<input th:field="*{firstName}" type="text" />
</div>
<div th:classappend="${#fields.hasErrors('first')}?has-error">
<label class="control-label">Email</label>
<input th:field="*{email}" ftype="text" />
</div>
<input type="submit" value="Save" />
</form>
</div>
<div th:fragment="info-success" xmlns:th="http://www.thymeleaf.org" th:remove="tag">
<p>Form successfully submitted</p>
</div>
JS 代码将简单地将表单提交到表单操作属性中提供的 URL。当响应返回到 JS 回调时,检查是否有任何错误。如果有错误,请将表格替换为响应中的表格。
(function($){
var $form = $('#userInfo');
$form.on('submit', function(e) {
e.preventDefault();
$.ajax({
url: $form.attr('action'),
type: 'post',
data: $form.serialize(),
success: function(response) {
// if the response contains any errors, replace the form
if ($(response).find('.has-error').length) {
$form.replaceWith(response);
} else {
// in this case we can actually replace the form
// with the response as well, unless we want to
// show the success message a different way
}
}
});
})
}(jQuery));
同样,这只是一个基本示例。正如上面评论中提到的,没有正确或错误的方法来解决这个问题。这也不是我的首选解决方案,我肯定会对此进行一些调整,但总的想法是存在的。
注意:我的 JS 代码也有缺陷。如果您用响应中的表单替换表单,表单提交处理程序将不会应用于新替换的表单。如果走这条路,您需要确保在替换表单后正确地重新初始化表单处理程序。
这方面的文档很少,但如果您已经熟悉 Web Flow,则可能不需要更多。我不确定这种技术如何与 Thymeleaf 中的正常 bean 绑定一起使用。我很想看到一个完整的宠物诊所演示应用程序使用它,这样我就可以看到控制器。
不确定这是否可以被视为最佳实践,但这是我所做的:
Map<String, String> errorMap = binding.getFieldErrors()
.stream().collect(Collectors.toMap(
e -> e.getField(), e -> messageSource.getMessage(e, locale)));
然后我将地图发送回 ajax 响应以在 "success" 部分处理。
不知道是否还有人感兴趣,但为了任何正在寻找示例解决方案的人,我将把它留在这里。
我实施了 yorgo 的解决方案并通过在片段中包含脚本来绕过“缺陷”。还有另一个小缺陷,如果有多个带有额外参数的提交按钮(通常用于在 spring + thymeleaf 中实现动态表单),则在序列化表单时信息会丢失。我通过手动将此信息附加到序列化表单来解决这个问题。
您可以在此处查看我对 yorgo 解决方案的实施及其修复:https://github.com/Yepadee/spring-ajax/blob/master/src/main/resources/templates/new_project.html
它的好处在于,您只需执行以下操作即可在任何现有的 thymeleaf 表单上实现它:
- 将脚本放入表单并使其引用表单的 id
- 将表单放入片段
- 将控制器中的 post 方法更改为 return 表单片段而不是整个模板