Thymeleaf #lists.contains() expression utility not working

35,577

Solution 1

I had posed this answer in thymeleaf forum and got some help to determine the root cause and resolution. See [http://forum.thymeleaf.org/Problem-with-thymeleaf-expression-utility-lists-contains-td4027317.html][1]

Essentially, the single character strings are being interpreted as a character type by default and therefore never match any of the strings in the list. Multi-character strings evaluate to a string type therefore work as expected. By encapsulating the value being searched in html encoded quotes, the parser is forced to evaluate the single character string as a string type instead of a char type. For example:

<div th:text="${#lists.contains(testList, &quot;3&quot;)}"/>
<div th:text="${#lists.contains(testList, &quot;P&quot;)}"/>

Just wanted to post this in case anyone was interested in the root cause and solution.

Solution 2

i know the question is old, but i post this answer so can be useful to others users having the same problem.

i don't know if it's you or another user, but here i found that we must add

'' + template.id

so:

th:checked="${#lists.contains(product.selectedTemplates, '' + template.id)}" 

for me it worked! Thank you

Solution 3

I did some further experimentation and found an issue where the #lists.contains() utility method does not work for a single character string. I would add that I'm using thymeleaf 2.0.19, but also tried 2.1.1.RELEASE with the same result.

I created a simple list in my model and added some values:

@FormParam("testList") 
private List<String> testList = Lists.newArrayList(); 

testList.add("test1"); 
testList.add("test2"); 
testList.add("3"); 
testList.add("P"); 
testList.add("33");

Then tested the #lists.contains() method like so:

<div th:text="${product.testList}"/>
<div th:text="${#lists.contains(product.testList, 'test1')}"/>
<div th:text="${#lists.contains(product.testList, 'test2')}"/>
<div th:text="${#lists.contains(product.testList, '3')}"/>
<div th:text="${#lists.contains(product.testList, 'P')}"/>
<div th:text="${#lists.contains(product.testList, '33')}"/>

And the output is as follows:

<div>[test1, test2, 3, P, 33]</div>
<div>true</div>
<div>true</div>
<div>false</div>
<div>false</div>
<div>true</div>

So, clearly the method does not work for single character strings. Because I'm working on a new project I simply reset the sequences driving these ids so that I don't have any single character id's to work with. That is certainly not the solution I was hoping for but it works. I will also add that in order to get the method to work in the context of my question, I had to add an empty character to my id like so:

th:checked="${#lists.contains(product.selectedTemplates, '' + template.id)}"

Without it, the contains method would return "false" because template.id is a Long type.

Share:
35,577
Tom Lerma
Author by

Tom Lerma

Updated on July 09, 2022

Comments

  • Tom Lerma
    Tom Lerma almost 2 years

    I'm working with the thymeleaf standard dialect and trying to render a list of checkboxes in a form. The rendering is ok, however, the problem is where I try to apply the "checked" property to the checkboxes using the thymeleaf #lists.contains() expression utility method.

    So I have a model class that has the following fields:

    private List<Template> templates;
    
    @FormParam("selectedTemplates")
    private List<String> selectedTemplates = Lists.newArrayList();
    

    A Thymeleaf template html fragment:

    <div th:each="template : *{templates}">
        <input type="checkbox" name="selectedTemplates" th:value="${template.id}" 
        th:checked="${#lists.contains(product.selectedTemplates, template.id)}" />
        <label th:text="${template.filename} + ' (' + ${template.description} + ')'" />
        <!-- Attempt to use the list contains to check the field -->
        <div th:text="${product.selectedTemplates}"/>
        <div th:text="${template.id}"/>  
        <div th:text="${#lists.contains(product.selectedTemplates, template.id)}" />
    </div>
    

    The output on the page for one of the checkboxes that should be selected.

    <input type="checkbox" name="selectedTemplates" value="4" /> (Template Name)
    <div>[4,5]</div>
    <div>4</div>
    <div>false<div>
    

    So as you can see, I print the list which has values [4,5] and I use the #lists.contains method to see if it has template.id in it, however, the method always returns false. I even tried some hard coded ids to test the method and I always get "false" back.

    For example:

    <div th:text="${product.selectedTemplates}"/>
    <div th:text="${#lists.contains(product.selectedTemplates, 4)}" />
    

    Prints [4,5]false

    <div th:text="${product.selectedTemplates}"/>
    <div th:text="${#lists.contains(product.selectedTemplates, '4')}" />
    

    Prints [4,5]false

    Not sure what I'm doing wrong, but it seems so straight forward, not sure what else to try. My guess is there is something wrong with the syntax. Any suggestions or advice is greatly appreciated. I'm not able to find any resources on troubleshooting this problem, the thymeleaf guide quickly glosses over that section.

  • Tom Lerma
    Tom Lerma over 10 years
    List<String> selectedTemplates is only the id's not the "template" entity. I hadn't thought of going that route. I found a work around which I will post in an answer.
  • Martin Frey
    Martin Frey over 10 years
    That is pretty strange. As far as i can see in the code of ListUtils the contains method is merely calling contains on the target list. In your case that would be equals to ${product.targetList.contains('3')}. Can you check if this gives the result correctly? In case of 3 i could imagine that its suddenly treated as an int and then the list would really not contain it, but with the 'P' that should return really true.
  • Martin Frey
    Martin Frey over 10 years
    Or even worse the 'P' is treated as a char. No clue if this would return true for a string list contains
  • Tom Lerma
    Tom Lerma over 10 years
    Thanks for the feedback Martin, I tried the code you suggested: ${product.testList.contains('3')}, ${product.testList.contains('P')}, ${product.testList.contains('33')} And the results were the same: "false", "false", "true" so perhaps it is that it is trying to infer a type incorrectly and thus not working with a single character.