Reputation: 21386
It's 2020 and Thymeleaf still doesn't support Optional
from Java 8, released six year ago. What are the workarounds?
I'm using Spring Boot 2.3.3.RELEASE with Thymeleaf. I have an object of type Foo
that the controller provides via the model/session as foo
. It has a method findName()
that returns Optional<String>
. So I can put this in my template:
[[${foo.findName()}]]
Assuming the name is "bar"
, that shows:
Optional[bar]
That works, but obviously it's not what I want.
So I tried what I would do in Java. The following should print simply the name (e.g. foo
), or _____
if the name is missing:
[[${foo.findName().orElse("_____")}]]
Unfortunately that produces an exception:
org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as expression: ""
Trying to use conditions, this will provide the value if it is present:
[[${foo.findName().isPresent()} ? ${foo.findName().get()}]]
But for some reason this throws an exception:
[[${foo.findName().isPresent()} ? ${foo.findName().get()} : '_____']]
First, I'm not sure why the example using orElse()
doesn't work, as the Optional
instance is an object like any of the others. Secondly, does anyone know a workaround? Thirdly, does anyone know of a workaround that is concise and doesn't take up three times the code?
(What we really need is native support in Thymeleaf so that ${foo.findName()} ?: '_____'
will just work naturally.)
Upvotes: 2
Views: 3271
Reputation: 44408
Since Thymeleaf uses Spring Epression language while integrated with Spring, the Thymeleaf itself is not to blame. As of the current version of Spring, the SpEl doesn't support Java 8 Optional yet, here is the GitHub issue #20433.
Possible workarounds require different expressions:
_
escaping issue is resolved throught a comment)
<span th:text="${foo.findName().orElse('---')" />
th:if
:
<th:block th:if="${foo.findName().isPresent()}">
<span th:text="${foo.findName().get()" />
</th:block>`
Upvotes: 3
Reputation: 170
As workaround for the Java Optionals funcionality use the "Elvis Operator" and the "Safe Navigation Operator" of SpringEL:
Safe Navigation Operator: If myOptional is null, this will return null instead of an error:
myOptional.orElse(null)?.myProperty
That's equivalent to this in Java:
myOptional.map(ObjectType::getMyProperty).orElse(null);
Elvis Operator: If myOptional or myProperty is null, this will return "alternativeContent":
myOptional.orElse(null)?.myProperty?:"alternativeContent"
That's equivalent to this in Java:
myOptional.map(ObjectType::getMyProperty).orElse("alternativeContent");
Easier might be, not to pass Optionals to Thymeleaf alltogether and only use the Operators mentioned above:
myObject?.myProperty
myObject?.myProperty?:"alternativeContent"
Upvotes: 0
Reputation: 21386
The only thing I can get to work so far is this:
<th:block th:switch="${foo.findName().isPresent()}">
<th:block th:case="true">[[${foo.findName.get()}]]</th:block>
<th:block th:case="*">_____</th:block>
</th:block>
Horrors—so verbose! Is that the best that can be done?
Upvotes: 1