How to change Thymeleaf fragments dynamically depending on the href link - java

I want to build a page with Thymeleaf fragments, which are rendered dynamically depending on the followed link.
And I have an issue with resolving fragments names in runtime through the Spring controller.
Here is the minimal reproductive example to show what I mean.
I have list.html page with two links List 1 and List 2 with pic and code as below:
<body>
<button type="button"><a th:href="${'/lists/list1'}">List 1</a></button>
<button type="button"><a th:href="${'/lists/list2'}">List 2</a></button>
<!-- This doesn't work, the include is not resolved correctly -->
<th:block th:include="'fragments/lists/' + ${fragmentName}
+ ' :: ' + ${fragmentName}"></th:block>
<!-- However static include works well -->
<th:block th:include="fragments/lists/list1 :: list1"></th:block>
</body>
The related Spring controller looks as:
#GetMapping("lists")
public String getListsPage(Model model) {
model.addAttribute("fragmentName", "listAll");
return "lists";
}
#GetMapping("lists/list1")
public String getAllItems(Model model) {
model.addAttribute("list1", itemService.getList1());
model.addAttribute("fragmentName", "list1");
return "lists";
}
#GetMapping("lists/list2")
public String getAllItems(Model model) {
model.addAttribute("list2", itemService.getList2());
model.addAttribute("fragmentName", "list2");
return "lists";
}
The problem that fragmentName is not being resolved at runtime and it throws TemplateInputException exception:
Caused by: org.thymeleaf.exceptions.TemplateInputException: Error resolving
template ['fragments/lists/' + ${fragmentName} + '], template might not exist
or might not be accessible by any of the configured Template Resolvers
(template: "lists" - line 38, col 11)
At the same time static block works correctly as shown in list.html page code.
Please don't suggest me Spring MVC 3.2 Thymeleaf Ajax Fragments, I don't want to use AJAX, I found the current solution of returning the fragment name using controller very clear and simple for my case.
Probably I can use Fragment Expressions, but I am not sure how exactly.
Any suggestions are appreciated.

I would express the syntax like this:
<th:block th:include="~{${'fragments/lists/' + fragmentName} :: ${fragmentName}}"></th:block>

Related

Thymeleaf template and Spring Boot : Creating a radio input from Java enum

I would like to populate a radio input control in a Thymeleaf automatically from a Java enum type called "source". I am using Spring Boot in the backend.
My controller initialises a list of the enum values like this:
this.sourcesAsList = Arrays.asList(Source.values());
model.addAttribute("sources", sourcesAsList);
This works fine, at least as far as the List is concerned (as I can see in the log output).
The Thymeleaf template then tries to instantiate a radio control based on this attribute in the model, like so:
<div th:each="source : ${sources}">
<input type="radio" th:field="*{source}" th:value="${source.value}"/><label th:text="| ${source.value} |">Should not see this !</label>
</div>
However, when I try to load this page, I get the following error:
[Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/feedbackapp2.html]")] with root cause
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'source' available as request attribute
The enum is pretty simple:
public enum Source {
VALONE, VALTWO, VALTHREE, VALFOUR;
public String getName() {
return this.name();
}
}
It's the first time I work with Thymeleaf, so I guess it is just a simple syntax problem somewhere, but even after googling I couldn't find a working example. Any ideas? Is it possible at all to do this with an enum? If not, what kind of datatype would be more appropriate?
Thanks a lot.
Cheers,
Martin
I played around a bit more and got it working. The following HTML snippet displays the radio buttons correctly based the list of Enums and also is wired to the model correctly, in that I received the selected value in the controller's POST method:
<div th:each="source : ${sources}">
<input name="source" type="radio" th:value="${source}"/><label for="source" th:text="| ${source} |">Something is wrong !</label>
</div>
There were two problems:
it is not necessary to access the name() attribute of the enum (so,
using ${source} instead of ${source.name} is fine)
instead of th:field, use the namne attribute of the input control
Thanks a lot to Periklis for the comment.

Thymeleaf in IntelliJ: cannot resolve variables

Intellij Not Recognizing Model Variables in HTML. How to resolve model variables. I don't get any idea for this issue.
Here is my Controller
#Controller
public void someController {
#RequestMapping("/")
public String someMethod() {
model.addAttribute("message", "message");
return "index";
}
And here is my "index.html"
<p th:text="${message}"> </p>
and of course in my html tag i'm using thymeleaf :
<html xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/html">
the problem is in myth:text="${message}" i see red squiggly lines saying that "Cannot resolve "message" variable..."
For recent versions of IntelliJ:
With the cursor on the variable, press Alt-Enter and you should see a menu option to "Declare external variable in comment annotation". When you select this option, you'll get a comment template with the cursor positioned to type in the data type of the variable.
When complete, you'll have something that looks like this:
<!--/*#thymesVar id="productIds" type="java.util.Map"*/-->
<div data-th-each="p : ${productIds}">
The Alt-enter menu doesn't seem to work within expressions such as ${#maps.isEmpty(productIds)}. In this case, manually creating the comment might make the UI get rid of the "unresolved" indicator.
I've been ignoring that issue for as long as I've been using Thymeleaf. Even though it shows the squiggly lines, it should still work when you run the application.
IntelliJ would almost have to compile the code in the background to be able to automatically (and accurately, since you could have multiple methods who uses the same template) resolve the variables.
I've never given a tip like this, but after reading your comment that you just find the wiggly line annoying, I decided to suggest it anyways:
Disable the tip.
I feel absolutely barbaric for posting this answer, forgive me SO
these red lined could be very annoying I just removed www. from my thymleaf and it worked
In your .html thymeleaf file Replace line 1 with line 2
Line1 : <html xmlns:th="http://www.thymeleaf.org">
Line2 : <html xmlns:th="http://thymeleaf.org">
Just stumbled upon this while having the same problem.
In my case it was caused by having the #SpringBootApplication class not in the same Maven module as the template and controller. Including the application module in the pom.xml with <scope>provided</scope> solved it for me.
I think #TwiN is right. IntelliJ indeed has to compile the whole thing and since it couldn't find an application it had no idea how to make a component lookup.

Thymeleaf Map<String, Object> iteration and non-english chars in map key

I have Spring, Thymeleaf, HTML5 web page. And have problem iterating through Map if map object contains non-english characters.
All the character encoding filters/resolvers/converters are set and forced to UTF-8
Let's say I have the following setup:
Controller:
#RequestMapping(value = "/app", method = { RequestMethod.GET, RequestMethod.POST })
public String view(ModelMap model, #RequestParam(value = "foo", required = false) Integer foo) {
MapDTO mapDto = new MapDTO();
Map<String, List<Foo>> mapFoo ... // populate map etc.
model.add("mapDto", mapDto.setMapFoo(mapFoo))
return foo == null ? VIEW : VIEW + " :: fooFragment"
}
And template for testing purposes:
.. page ..
<div th:fragment="fooFragment" id="fooFragment">
..
<th:block th:each="fooMap : ${mapDto.mapFoo}">
<th:block th:each="item,row : ${fooMap.value.fooList}">
<p th:text="${item.val}"</p> <!-- working OK -->
<p th:text=" ${fooMap.value.fooList[__${row.index}__].idrValueName} "></p> <!-- working OK -->
<input th:field="*{mapDto.mapFoo[__${fooMap.key}__].fooList[__${row.index}__].val} "></p><!-- And this is working -->
<p th:text="${mapDto.mapFoo[__${fooMap.key}__].fooList[__${row.index}__].val} "></p><!-- FAILING ON THIS -->
</th:block>
</th:block>
..
</div>
.. rest of the page ..
Let's say, that i have map with keys "A" and "Ā".
I'm getting following exception:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "mapDto.mapFoo[Ā].fooList[0].val"
..
Caused by:
java.lang.IllegalStateException: Cannot handle (257) 'Ā'
Whats interesting - I have almost exactly the same setup on different page (A bit different mapDto structure and template, but the same principle, same page headers) and I don't have the same issues there.
Maps that don't have keys with non-english characters behaves as predicted.
Any ideas where lies the problem?
SOLVED:
Solved described issue using double-bracket syntax
${{...}}
There are some limitations with it. But I was able to work around them it solved my main issue.
mapDto.mapFoo[Ā].fooList[0].val
This expression contains accented character Ā. Looking at the source code of Tokenizer, accented characters are clearly indicated as an invalid candidate for tokenization.
Spring EL's InternalSpelExpressionParser which is responsible for parsing expressions is tightly coupled to this Tokenizer, so I don't think there is an out of the box way to allow tokenization of accented characters. I'm pretty sure there is a good reason why this is not allowed (e.g. it might break other parts of the framework) so I suggest you go along to what the framework is demanding and remove the accented characters in the expression.

How do I disable escaping of a URL (particularly the &) in a Thymeleaf template?

I'm passing a bean QuoteOption into the model that has a method called getCachedImageUrl(url, width, height) that returns a URL for the image.
My Thymeleaf template:
<img th:src="${quoteOption.getCachedImageUrl(baseUrl,300,200)}" />
Generates
<img src="http://nitro:8080/image/nitro-resources-development/28f08e67-96c9-4bb4-8012-9e34040cc976.jpeg?width=300&height=200" />
Notice the & in the url parameters! Aaaargh! How do I instruct Thymeleaf to not escape the ampersand?
Bean with name someBean:
public class SomeBean {
public String testMethod() {
return "http://nitro:8080/image/nitro-resources-development/28f08e67-96c9-4bb4-8012-9e34040cc976.jpeg?width=300&height=200";
}
}
In Thymeleaf :
<img th:src="${#applicationCache.testMethod()}" id="_Bhuwan"/>
<button onclick="alert($('#_Bhuwan').attr('src'))">See</button>
Two Case :
In alert Show message as :
http://nitro:8080/image/nitro-resources-development/28f08e67-96c9-4bb4-8012-9e34040cc976.jpeg?width=300&height=200
In View Page Source or Edit HTML in Browser. Generate :
<img src="http://nitro:8080/image/nitro-resources-development/28f08e67-96c9-4bb4-8012-9e34040cc976.jpeg?width=300&height=200">
So that actual value in src attribute of img tag is http://nitro:8080/image/nitro-resources-development/28f08e67-96c9-4bb4-8012-9e34040cc976.jpeg?width=300&height=200 in this case. But when we try see value using View Page source or edit html through browser then Browser may escape & char to &.
Tested On
Themeleaf version is 2.1.4.RELEASE in Spring 4.

Spring MVC does not render html with angularjs directive

the angularjs feature called directive cause to problems in spring mvc. If I use thymeleaf to render a html with elements such
<div ui-view autoscroll="false"></div>
i got a error like
org.xml.sax.SAXParseException: Attribute name "ui-view" associated with an element type "div" must be followed by the ' = ' character.
is there an elegant workaround or should I use something else than thymeleaf?
Edit:
Many thanks for your answers, they helped me a lot.
Either you code xml or you use some workaround. open your application.properties and add following
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=LEGACYHTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
spring.thymeleaf.cache=false
Just put the below code. basically what it is saying is that every attribute in HTML should have a value. When the browser renders it, it will anyway look like below.
<div ui-view="" autoscroll="false"></div>
Update: You can also use directive in a class or as an element.

Categories

Resources