Spring MVC 3.2 Thymeleaf Ajax 片段
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20982683/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring MVC 3.2 Thymeleaf Ajax Fragments
提问by y0gie007
I'm building application with Spring MVC 3.2 and Thymeleaf templating engine. I'm a beginner in Thymeleaf.
我正在使用 Spring MVC 3.2 和 Thymeleaf 模板引擎构建应用程序。我是 Thymeleaf 的初学者。
I have everything working, including Thymeleaf but I was wondering if anyone knows of a simple and clear toturial on how to do simple Ajax request to controller and in result rendering only a part of a template (fragment).
我一切正常,包括 Thymeleaf,但我想知道是否有人知道关于如何对控制器执行简单的 Ajax 请求并仅呈现模板(片段)的一部分的简单而清晰的教程。
My app has everything configured (Spring 3.2, spring-security, thymeleaf, ...) and works as expected. Now I would like to do Ajax request (pretty simple with jQuery but I don't wan't to use is since Thymeleaf in its tutorial, chapter 11: Rendering Template Fragments (link) mentiones it can be done with fragments.
我的应用程序已配置好所有内容(Spring 3.2、spring-security、thymeleaf 等)并且按预期工作。现在我想做 Ajax 请求(使用 jQuery 非常简单,但我不想使用,因为 Thymeleaf 在其教程第 11 章:渲染模板片段(链接)中提到它可以用片段来完成。
Currently I have in my Controller
目前我在我的控制器中
@RequestMapping("/dimensionMenuList")
public String showDimensionMenuList(Model model) {
Collection<ArticleDimensionVO> articleDimensions;
try {
articleDimensions = articleService.getArticleDimension(ArticleTypeVO.ARTICLE_TYPE);
} catch (DataAccessException e) {
// TODO: return ERROR
throw new RuntimeException();
}
model.addAttribute("dimensions", articleDimensions);
return "/admin/index :: dimensionMenuList";
}
the part of the view where I would like to replace <ul></ul>menu items:
我想替换<ul></ul>菜单项的视图部分:
<ul th:fragment="dimensionMenuList" class="dropdown-menu">
<li th:unless="${#lists.isEmpty(dimensions)}" th:each="dimension : ${dimensions}">
<a href="#" th:text="${dimension.dimension}"></a>
</li>
</ul>
Any clue is greatly appreciated. Especially if I don't have to include any more frameworks. It's already too much for java web app as it is.
任何线索都非常感谢。特别是如果我不必包含更多框架。对于 Java Web 应用程序来说,这已经太多了。
回答by Sohail
Here is an approach I came across in a blog post:
这是我在博客文章中遇到的一种方法:
I didn't want to use those frameworks so in this section I'm using jQuery to send an AJAX request to the server, wait for the response and partially update the view (fragment rendering).
我不想使用这些框架,所以在本节中,我使用 jQuery 向服务器发送 AJAX 请求,等待响应并部分更新视图(片段渲染)。
The Form
表格
<form>
<span class="subtitle">Guest list form</span>
<div class="listBlock">
<div class="search-block">
<input type="text" id="searchSurname" name="searchSurname"/>
<br />
<label for="searchSurname" th:text="#{search.label}">Search label:</label>
<button id="searchButton" name="searchButton" onclick="retrieveGuests()" type="button"
th:text="#{search.button}">Search button</button>
</div>
<!-- Results block -->
<div id="resultsBlock">
</div>
</div>
</form>
This form contains an input text with a search string (searchSurname) that will be sent to the server. There's also a region (resultsBlock div) which will be updated with the response received from the server.
此表单包含一个带有搜索字符串 (searchSurname) 的输入文本,该文本将被发送到服务器。还有一个区域(resultsBlock div),它将使用从服务器收到的响应进行更新。
When the user clicks the button, the retrieveGuests() function will be invoked.
当用户单击按钮时,将调用retrieveGuests() 函数。
function retrieveGuests() {
var url = '/th-spring-integration/spring/guests';
if ($('#searchSurname').val() != '') {
url = url + '/' + $('#searchSurname').val();
}
$("#resultsBlock").load(url);
}
The jQuery load function makes a request to the server at the specified url and places the returned HTML into the specified element (resultsBlock div).
jQuery 加载函数在指定的 url 处向服务器发出请求,并将返回的 HTML 放入指定的元素 (resultsBlock div) 中。
If the user enters a search string, it will search for all guests with the specified surname. Otherwise, it will return the complete guest list. These two requests will reach the following controller request mappings:
如果用户输入搜索字符串,它将搜索具有指定姓氏的所有客人。否则,它将返回完整的客人名单。这两个请求将到达以下控制器请求映射:
@RequestMapping(value = "/guests/{surname}", method = RequestMethod.GET)
public String showGuestList(Model model, @PathVariable("surname") String surname) {
model.addAttribute("guests", hotelService.getGuestsList(surname));
return "results :: resultsList";
}
@RequestMapping(value = "/guests", method = RequestMethod.GET)
public String showGuestList(Model model) {
model.addAttribute("guests", hotelService.getGuestsList());
return "results :: resultsList";
}
Since Spring is integrated with Thymeleaf, it will now be able to return fragments of HTML. In the above example, the return string "results :: resultsList" is referring to a fragment named resultsList which is located in the results page. Let's take a look at this results page:
由于 Spring 与 Thymeleaf 集成,因此它现在能够返回 HTML 片段。在上面的示例中,返回字符串“results :: resultsList”指的是位于结果页面中的名为 resultsList 的片段。我们来看看这个结果页面:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
</head>
<body>
<div th:fragment="resultsList" th:unless="${#lists.isEmpty(guests)}" id="results-block">
<table>
<thead>
<tr>
<th th:text="#{results.guest.id}">Id</th>
<th th:text="#{results.guest.surname}">Surname</th>
<th th:text="#{results.guest.name}">Name</th>
<th th:text="#{results.guest.country}">Country</th>
</tr>
</thead>
<tbody>
<tr th:each="guest : ${guests}">
<td th:text="${guest.id}">id</td>
<td th:text="${guest.surname}">surname</td>
<td th:text="${guest.name}">name</td>
<td th:text="${guest.country}">country</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
The fragment, which is a table with registered guests, will be inserted in the results block.
该片段是一个带有注册客人的表,将被插入到结果块中。
回答by Abdullah Khan
Rendering only Thymeleaf fragmentsalso works well with ModelAndView.
仅渲染Thymeleaf fragments也适用于ModelAndView.
Your controller
您的控制器
@RequestMapping(value = "/feeds", method = RequestMethod.GET)
public ModelAndView getFeeds() {
LOGGER.debug("Feeds method called..");
return new ModelAndView("feeds :: resultsList");
}
Your view
你的看法
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head></head>
<body>
<div th:fragment="resultsList" id="results-block">
<div>A test fragment</div>
<div>A test fragment</div>
</div>
</body>
</html>
What's actually rendered
实际呈现的内容
<div id="results-block">
<div>A test fragment</div>
<div>A test fragment
</div>
</div>
回答by Jorge.V
As an alternate version to Sohail's great answer, I want to give a version that using javascript can send the whole th:object to the controller, integrating Thymeleaf in your forms, not having to use @PathVariable which becomes messy or not usable at all when you've forms with many fields.
作为 Sohail 出色答案的替代版本,我想提供一个版本,使用 javascript 可以将整个 th:object 发送到控制器,将 Thymeleaf 集成到您的表单中,而不必使用 @PathVariable,这会变得凌乱或根本无法使用你有很多领域的表格。
For the form (using an example which returns an object which has an id and a name Strings, and feeds a combobox with a Map that has some of those objects as values) we have:
对于表单(使用一个示例,该示例返回一个对象,该对象具有一个 id 和一个名称字符串,并提供一个带有 Map 的组合框,该 Map 将其中一些对象作为值)我们有:
<form method="post" th:action="@{/yourMapping}" th:object="${object}" id="yourFormId">
<select th:field="*{mapOfObjects}">
<option
th:each="entry: ${mapOfObjects}"
th:value="${entry.value.id}"
th:text="${entry.value.name}" >
</option>
</select>
<p>Name:
<input type="text" th:field="*{name}" />
</p>
</form>
When this form is submited (using a button with type submit for example) the whole document will be replaced. However we can intercept this submit with javascript and do it the ajax-way. To achieve this, we will add an interceptor to our form using a function. First call the function that adds the interceptor right after the form:
当提交此表单时(例如使用带有类型提交的按钮),整个文档将被替换。但是,我们可以使用 javascript 拦截此提交并以 ajax 方式进行。为了实现这一点,我们将使用一个函数向我们的表单添加一个拦截器。首先调用在表单之后添加拦截器的函数:
<script>formInterceptor("yourFormId");</script>
And the function looks like this (place it in the head of the document or wherever suits your needs):
函数看起来像这样(将它放在文档的头部或任何适合您需要的地方):
<script>
function formInterceptor(formName) {
var $form = $("#" + formName);
$form.on('submit', function(e) {
e.preventDefault();
$.ajax({
url : $form.attr('action'),
type : 'post',
data : $form.serialize(),
success : function(response) {
if ($(response).find('.has-error').length) {
$form.replaceWith(response);
}
else{
$("#ajaxLoadedContent").replaceWith(response);
}
}
});
});
};
</script>
Now whenever the form is submited, this function will trigger, and it will:
现在每当提交表单时,这个函数就会触发,它会:
- Prevent the original form submit
- Make an ajax call using the url defined in the form's th:action
- Serialize the form data. Your controller will be able to recieve this in an object
- Replace the part of your html code with the returned fragment
- 防止原始表单提交
- 使用表单的 th:action 中定义的 url 进行 ajax 调用
- 序列化表单数据。您的控制器将能够在对象中接收此信息
- 用返回的片段替换 html 代码的一部分
The replaced part should look like this
被替换的部分应该是这样的
<div id="ajaxLoadedContent"></div>
And the controller can recieve the th:object in the form, with it's values filled, like this (Replace Object with your object's type and "object" with a proper name):
并且控制器可以接收表单中的 th:object,填充它的值,如下所示(用您的对象类型替换 Object,用正确的名称替换“object”):
@PostMapping(value = /yourMapping)
public String ajaxCtrlExample(@ModelAttribute("object") Object object, Model model) {
return yourFragmentPath;
}
And that's everything. Call the function that adds the interceptor after every form you need in ajax-version.
这就是一切。在 ajax-version 中的每个表单之后调用添加拦截器的函数。

