6
votes

I am building an application using Spring, Hibernate and of course Thymeleaf for the views. The application is centered around an Activity domain class (which is literally an in- or outdoor activity organized by a club member). The class has several lookup fields (category region) which are modelled as domain classes mapped by Hibernate to database lookup tables.

In the Edit view page I show a dropdown box to be able to choose a lookup value, also the current value must be shown using the selected attribute. Here it goes wrong. The select attribute is never generated although the boolean expression appears to be correct.

The th:attr is added to show the values of the compared expressions, namely ${choice.id} and *{category.id} Those values are correct. I added the readonly attribute (not suitable for a select element but just to chech whether it could be a problem of the th:selected attribute) and we see in the output that this attribute IS generated in the output!

<label>Categorie</label>
<select th:field="*{category}">
  <option th:each="choice : ${allCategories}" 
          th:value="${choice.id}" 
         th:attr="choiceid=${choice.id}, categoryid=*{category.id}, showselected=(${choice.id} == *{category.id})" 
         th:selected="(${choice.id} == *{category.id})" 
         th:readonly="(${choice.id} == *{category.id})" 
         th:text="${choice.description}"></option>
</select>

HTML output:

<label>Categorie</label>
<select id="category" name="category">
  <option choiceid="1" categoryid="2" showselected="false" value="1">Actief en sportief</option>
  <option choiceid="2" categoryid="2" showselected="true" readonly="readonly" value="2">Uitgaan en nachtleven</option>
  <option choiceid="3" categoryid="2" showselected="false" value="3">Kunst en cultuur</option>
  <option choiceid="4" categoryid="2" showselected="false" value="4">Eten en drinken</option>
  <option choiceid="5" categoryid="2" showselected="false" value="5">Ontspanning en gezelligheid</option>
</select>

So my questing boils down to: why is selected="selected"

not generated for option element with id=2?

Of course it can be achieved by generating is with an th:attr expression but according to the documentation it should just work in this way.

Also I tried to bypass the use of the special th:selected attribute and use th:attr to generate the selected attribute for the selected option (the value 'none' is temporary used for clarity'):

th:attr="selected=(${choice.id} == *{category.id}) ? 'selected' : 'none'"

Again, nothing is shown and no selected attribute is rendered. I tried to repeat is with a custom attribute name (the Dutch translation of the word selected, of course unknown to Thymeleaf):

th:attr="geselecteerd=(${choice.id} == *{category.id}) ? 'selected' : 'none'"

And now it is rendered! See the output below:

<select id="category" name="category">
  <option geselecteerd="none" value="1">Actief en sportief</option>
  <option geselecteerd="selected" value="2">Uitgaan en nachtleven</option>
  <option geselecteerd="none" value="3">Kunst en cultuur</option>
  <option geselecteerd="none" value="4">Eten en drinken</option>
  <option geselecteerd="none" value="5">Ontspanning en gezelligheid</option>
</select>

I am really lost now, the selected attribute is perfectly legal and the only choice here but it appears that Thymeleaf is ignoring it. How to solve this?

6

6 Answers

16
votes

according to the thymeleaf forum (post) th:field doesn't work with th:selected

try something like this (not tested)

<select name="category">
    <option th:each="choice : ${allCategories}" 
     th:value="${choice.id}" 
     th:attr="choiceid=${choice.id}, categoryid=*{category.id}, showselected=(${choice.id} == *{category.id})" 
     th:selected="(${choice.id} == *{category.id})" 
     th:readonly="(${choice.id} == *{category.id})" 
     th:text="${choice.description}"></option>
</select>
5
votes

'th: field' automatically generates the 'id' and 'name' attributes, so if you want to use 'th: selected', you should remove 'th: field' and manually set them to work.

I had the same problem and check the forum, 'th: selected' and 'th: field' does not work at the same time. check the forum

<div class="row">
<div class="input-field col s12">
    <select id="doc" name="doc" th:with="doc=*{doc}">
                    <option value="" th:disabled="disabled" selected="true"
                       th:text="${status==true}? 'Seleccione tipo de documento' : ${doc}">Seleccione
                       tipo de documento</option>
                    <option th:each="tipoDoc : ${tipoDocs}" th:text="${tipoDoc}"
                       th:value="${tipoDoc}" />
              </select>
    <label>Documento</label>
</div>

3
votes

This solutions is working for me

<div class="form-group">
<label>Employee's Status</label>
<select id="statusid" name="statusid" th:field="*{statusid}" class="form-control col-md-7 col-xs-12"  required="true" />
<option th:value="''" th:text="Select"></option>
<option th:each="i : ${allStatus}" th:value="${i.id}" th:text="${i.statusname}" th:selected="${i.id} == ${employee.statusid}"></option>
</select>
</div>
1
votes

I found that thymeleaf auto selected when I wanted to edit an item, it loaded form with predefined values, my code like this:

<select th:field="*{type}" class="form-control" name="stockType">
<option value="1">Type1</option>
<option value="2">Type2</option>
<option value="4">Type4</option></select>

Works like a charm

1
votes

I know its a bit late,. but anyone searching for the solution...

To use the *{fieldname} select option you should use the th:value with 2 curly brackets

<select th:field="*{roadmap}" required>
<option th:each="rm : ${roadmaps}" th:value="${{rm}}" th:text="${rm.title}">
</option>
</select>

You need to add a formatter:

public class RoadmapFormatter implements Formatter<Roadmap> {
    @Override
    public Roadmap parse(String id, Locale locale) throws ParseException {
        Roadmap r = new Roadmap();
        r.setId(Long.parseLong(id));
        return r;
    }

    @Override
    public String print(Roadmap r, Locale locale) {
        return String.valueOf(r.getId());
    }
}

Last but not least register the formatter in the configuration class:

@Configuration
public class Configuration implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry formatterRegistry) {
        formatterRegistry.addFormatter(new RoadmapFormatter());
    }
}
0
votes
---------entity------
public class ProductType {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "TypeId")
private Integer typeId;

//Categories ID
@ManyToOne
@JoinColumn(name = "CatId")
private Categories categories;

---------html-------

<select th:field="*{categories.catId}" >
                                
    <option   th:each="c: ${listCat}" th:value="${c.catId}"
             th:text="${c.catName}" >
     </option>

</select>

try this. its worked with me