Using conditional logic in th:object

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Using conditional logic in th:object

alex
I was using this directive:

<tbody th:object="${metrics} ? ${metrics} : ${metricsMap[listing.id]}">

It was working before I upgraded to the latest Thymeleaf and Spring, but now I am getting an error message from Spring / Thymeleaf that it's illegal. If I remove the conditional and leave either ${metrics} or ${metricsMap[listing.id]}, it is considered legal. But in my context I'm trying to share this bit of logic between two contexts where the object itself is held in two different places.

Is there a syntax change I can use? Otherwise, what's my best bet (without having to copy and paste this table into two places)?
Reply | Threaded
Open this post in threaded view
|

Re: Using conditional logic in th:object

Emanuel
Administrator
Re: possible syntax change - you could swap that logic with the Elvis operator:

th:object="${metrics} ?: ${metricsMap[listing.id]}"
Reply | Threaded
Open this post in threaded view
|

Re: Using conditional logic in th:object

alex
Emanuel, thanks for the suggestion, but I get the same error:

SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/mypath] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: The expression used for object selection is ${metrics} ?: ${metricsMap[listing.id]}, which is not valid: only variable expressions (${...}) are allowed in 'th:object' attributes in Spring-enabled environments. (include:510)] with root cause
org.thymeleaf.exceptions.TemplateProcessingException: The expression used for object selection is ${metrics} ?: ${metricsMap[listing.id]}, which is not valid: only variable expressions (${...}) are allowed in 'th:object' attributes in Spring-enabled environments. (include:510)
	at org.thymeleaf.spring4.processor.attr.SpringObjectAttrProcessor.validateSelectionValue(SpringObjectAttrProcessor.java:73)
	at org.thymeleaf.standard.processor.attr.AbstractStandardSelectionAttrProcessor.getNewSelectionTarget(AbstractStandardSelectionAttrProcessor.java:69)
	at org.thymeleaf.processor.attr.AbstractSelectionTargetAttrProcessor.processAttribute(AbstractSelectionTargetAttrProcessor.java:61)
Reply | Threaded
Open this post in threaded view
|

Re: Using conditional logic in th:object

danielfernandez
Administrator
Complex expressions in th:object are only disallowed in <form> tags, when using Spring with Thymeleaf.

This is because such th:object expressions must be a part of the form-binding expressions used by Spring for retrieving data from and setting data into form backing beans. And SpringEL's syntax for this does obviously not allow things such as complex thymeleaf conditional expressions.

But your example code uses a <tbody> tag, not a <form>... could you please confirm you are experiencing this error in a <tform>. This should be working for you, a restriction on tags other than <form> never existed, and I've just confirmed in works in 2.1.3 and 2.0.19... what version of thymeleaf are you using?
Reply | Threaded
Open this post in threaded view
|

Re: Using conditional logic in th:object

alex
Daniel, first off thanks for all your work on this framework and also for getting back to me.

Regarding your questions:

1. The problem I am experiencing is not in the context of a <form>. It is in the context of a .
2. I am using Thymeleaf with Spring and Apache Tiles
3. The structure of my tiles (if it matters) is root page -- includes --> main content -- includes --> this table
4. The library versions I'm using are Thymeleaf: 2.1.3.RELEASE and Tiles2.spring4: 2.1.1.RELEASE.

Let me know if you need any more details.
Reply | Threaded
Open this post in threaded view
|

Re: Using conditional logic in th:object

danielfernandez
Administrator
Actually, you are completely right. In my response, I mistakenly explained to you how this worked in Thymeleaf 2.0, not 2.1. Sorry for that.

Thymeleaf 2.1 had to restrict th:object in Spring applications to just simple variable expressions, this is, "${...}" ones, and your expression is a complex conditional expression (X ? Y : Z). The reason of this restriction is to enable compatibility with Spring's ConversionServices for data selected with a th:object. We are forced to restrict the expressions that select objects in th:object to those that are usable in a Spring object binding in order to be passed to a ConversionService. And this means restricting th:object to only Spring EL expressions, i.e. ${...}.

Your workaround should be easy: simply delegate your condition to the Spring EL engine (instead of the Thymeleaf Standard Expressions engine) by enclosing your whole expression between braces:

<tbody th:object="${metrics ? metrics : metricsMap[listing.id]}">

Note how Spring EL uses the same syntax for conditionals as Thymeleaf Standard Expressions (X ? Y : Z), so the change is quite subtle.