insert quotes into expression thymeleaf - spring-boot

I have a fragment of thymeleaf code with form. I want to validate this form.
<form>
<th:block th:fragment="input (label, name, type)">
<div class="col-md-3 form-group" th:class="${#fields.hasErrors(*{__${name}__})}
? 'col-md-3 form-group has-error' : 'col-md-3 form-group'">
Here I got exception:
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "#fields.hasErrors(name)" (template: "fragments/inputFieldWithType" - line 5, col 42)
Caused by: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'user' available as request attribute
As I seen in Baeldung, i have to bring field name in quotes like this:
${#fields.hasErrors('name')}
So how can I put name param into quotes?

You can use th:class="${#fields.hasErrors('__${fieldName}__')}"

Related

Exception evaluating SpringEL expression for String array

While at spring boot <= 2.4.3 the below snippet worked fine.
<div class="link-red ddmenu" th:with="urls=${new String[]{'/'}}"
th:classappend="${#arrays.contains(urls, #httpServletRequest.getRequestURI()) ? 'selected' : ''}">
<a href="/" th:href="#{/}"> <i class="fa fas fa-home"></i>Home
</a>
</div>
But after upgrading to 2.7.0, I am getting this parsing exception. What should I do to fix it?
Exception evaluating SpringEL expression: "new String[]{'/'}"
Or any related information for this break, available on any Thymeleaf's official site?
Interestingly enough, if you keep looking down the stack track you find this error at the bottom:
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1005E: Type cannot be found 'String'
If I change the expression to:
th:with="urls=${new java.lang.String[]{'/'}}"
Everything works again for me (tested with Spring Boot starter 2.6.7).

Thymeleaf expression parse exception

I've 2 lists; allkontrols (2 element), risk.kontrols (1 element)
I'm listing all kontrols as checkbox and trying to 'select' them if risk.kontrols list contains it.
The one element in risk.kontrols is also in allkontrols.
So, when I tried to print the results of this:
<div th:each="kontrol : ${allkontrols}" th:text="${#lists.contains(risk.kontrols, kontrol)}"/>
I got results as
true
false
Everything is ok till here. Now, when I tried to populate them with data, I got errors. Here is the code:
<div th:each="kontrol : ${allkontrols}" class="items form-control-lg">
<label>
<input th:value="${kontrol.id}" th:selected="{#lists.contains(risk.kontrols, kontrol)}" type="checkbox" class="flat">
<div style="display: inline-block;" th:utext=" ${kontrol.name}"></div>
</label>
</div>
Error part:
th:selected="{#lists.contains(risk.kontrols, kontrol)}"
Stacktrace:
2018-07-27 20:36:55 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "view/riskkontrol.html")] with root cause
org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as expression: "{#lists.contains(risk.kontrols, kontrol)}" (template: "riskkontrol" - line 44, col 140)
It looks like you're missing a $ before the { in your th:selected, try changing it to: th:selected="${#lists.contains(risk.kontrols, kontrol)}"

Spring Thyme Leaf checking for global errors results in NPE

To display global errors created with Spring MVC with Thyme Leaf, I tried the example given at http://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#global-errors:
That, is:
<div th:if="${#fields.hasGlobalErrors()}">
and
<ul th:if="${#fields.hasErrors('global')}">
and
<div th:if="${#fields.hasGlobalErrors()}">
When I add them to my HTML, the page won't even render, nevermind about submitting the form. All the examples result in:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "#fields.hasErrors('global')"
I tried this with v2.1.4 and v.2.1.3 and got the same error. Bug or am I doing something wrong?
Yes, the tags are all closed and properly formed. Yes, this code was within a form. Yes, all the other aspects of the form work without the global error check.
Here is a short version of broken HTML:
<form action="search.html" th:action="#{/auto/search}">
<p th:if="${#fields.hasErrors('global')}" th:errors="*{global}">
Incorrect date
</p>
<input type="text" th:field="${command.stockNumber}" />
<select th:field="*{command.startYear}">
<option value="" th:each="year : ${modelYears}" th:value="${year}"
th:text="${year}"></option>
</select>
</form>
And the controller..
#RequestMapping(value = "/auto/search", method = RequestMethod.POST)
public String search(#Validated
#ModelAttribute("command")
AutoSearchCommand autoSearchCommand
BindingResult result, Model model) {
return "search";
}
Solved:
th:object is needed in a tag preceding to the global error check. Unfortunately, this isn't mentioned in the Spring Thyme Leaf tutorial. Presumably, there's a default form name somewhere which I've overridden in my controller.
Adding the tag in results in this working html:
<form action="search.html" th:action="#{/auto/search}">
<div th:object="${command}" th:remove="tag">
<p th:if="${#fields.hasErrors('global')}" th:errors="*{global}">
Incorrect date
</p>
</div>
<input type="text" th:field="${command.stockNumber}" />
<select th:field="*{command.startYear}">
<option value="" th:each="year : ${modelYears}" th:value="${year}"
th:text="${year}"></option>
</select>
</form>
.. Where "command" is the name of the form bean in the controller.
This thread helped me figure it out.

Thymeleaf: How to use fragment parameter in expressions

Is there a way to use a fragment parameter in an expression?
I'd like to create a fragment to show fields with their corresponding binding errors e.g. like:
<div th:fragment="alert (field, fieldLabel)">
<label><span th:text="${fieldLabel}">Label:</span><input type="text" th:errorclass="field_error" th:field="*{field}"/></label>
<div th:if="${#fields.hasErrors(field)}"><span th:errors="*{field}">Some error</span></div>
</div>
Getting the fragment with:
<div th:replace=":: alert (field='firstName', fieldLabel='Firstname')">Field</div>
How do I use the field parameter in the expressions for the th:field and th:errors attributes? *{field} does at least not work.
With Thymeleaf 2.1 I've been using the following:
Declare field:
<input type="password" th:field="*{password}" />
Show possible errors related to password:
<div th:replace="util/form :: field-errors('password')"></div>
And this prints all the errors related to given field:
<div class="error-container help-block"
th:fragment="field-errors(field)"
th:if="${#fields.hasErrors('__${field}__')}">
<ul>
<li th:each="error : ${#fields.errors('__${field}__')}"
th:text="${error}" />
</ul>
</div>
It seems that it is not possible at least
using th:field and th:errors, it keeps trying to look for a bean instead
of the parameter of the fragment.
Try setting a local variable for the DOM object
th:with="variableName=${field}"
An then try to use that variable in the expressions.

Spring tag <form:option> not working with Spring Data DomainClassConverter?

I'm using Spring MVC and Spring Data and also have configured Spring Data's DomainClassConverter to automatically convert String id to the appropriate Domain class.
I'm now implementing a Order to Customer reference using the tag using:
<form:select path="customer">
<form:option value="" label="Select" />
<form:options items="${customers}" itemValue="id" />
</form:select>
which results in the given HTML:
<select id="customer" name="customer" class="span6">
<option value="">Select</option>
<option value="1">Customer A</option>
<option value="2">Customer B</option>
<option value="3">Customer C</option>
</select>
When submitting a post with e.g. Customer A selected I get a exception like:
org.apache.jasper.JasperException: org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type #javax.validation.constraints.NotNull #javax.persistence.ManyToOne nl.kapsalonreflection.domain.Customer for value ''; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: The given id must not be null!; nested exception is java.lang.IllegalArgumentException: The given id must not be null!
Which does not make sense at all as the value received should be 1 and not ''.
I also debugged the received request params and it only contained customer=1 (as expected)
Note that the InvalidDataAccessApiUsageException is coming from Spring Data's DomainClassConverter and if I remove the this converter the problem does not occur anymore.
I also debugged the DomainClassConverter and what I can see it tries to cover twice.
First with the String value 1 (as expected) but then another call with an empty String, causing the exception.
It get's weirder though...
When I replace <form:option value="" label="Select" /> with the plain html element <option value="">Select</option> then the exception does not occur, and hence only 1 call is made to the convert method with the String 1.
The weird part is that both <form:option value="" label="Select" /> and <option value="">Select</option> product the same html output...
I cannot explain the behaviour... although I seemm to have a 'workaround' with the plain html element I would like to know what is causing the issue.
Note that org.apache.jasper.JasperException indicates that exception occurs during JSP rendering, not during data binding. It's consistent with the fact that exception depends on <form:option value="" label="Select" /> - it's thrown when this tag is being processed when rendering the form after postback.
Behaviour you observe can be explained as follows: in order to determine its selected state <form:option> tries to compare its value with value of a field bound to <form:select>. If value of the bound field is null, <form:option> simply compares its value with null, that's why you don't get this exception during initial form rendering. Otherwise, <form:option> tries to convert its <value> to type of the bound field, and value = "" causes an exception at this step.
So, you should use null instead of empty string for "no option selected" value:
<form:option value="${null}" label="Select" />
You may need to consider attaching a propery editor in this kind of scenario, because if i see the code <form:options items="${customers}" itemValue="id" /> you are directly attaching the customers list to the options. I think its autmatically taking the toString and displaying the "Csutomer A", "Customer B" etc. Ideally you may need to use the itemLabel in the form:options tag or you may need to register a property editor and attach that in the initBinder http://static.springsource.org/spring/docs/2.5.x/reference/validation.html#beans-beans-conversion

Resources