23
votes

In Short

I want to have switch statement in thymeleaf with logic once written to multiple case statements.

In detail

I want to implement this in the thymeleaf

switch(status.value){
  case 'COMPLETE':
  case 'INVALID':
     //print exam is not active
     break;
  case 'NEW':
     //print exam is new and active
     break;
}

My current thymleaf code which fails with runtime error

 <div th:switch="${status.value}">
      <div th:case="'COMPLETE','INVALID'">
         <!-- print object is not active -->
      </div>
      <div th:case="NEW'">
         <!-- print object is new and active -->
      </div>
 </div>                             

But the above code fails with error

org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as expression: "'COMPLETE','INVALID'"...

Note: I know the reason for this above error message. All I need is to know a way to implement switch with multiple cases for a single output

3
There is no way to make it the way you want. Just use fragments in both cases if you wish to avoid code duplication, as pens-fan-69 stated. - Ostap Gonchar

3 Answers

50
votes

The failure is due to the fact that you don't have a valid expression in the first case. Specifically,

'COMPLETE','INVALID'

is not a valid expression. I suspect that what you are trying to do is include the div if the status is COMPLETE or INVALID. Unfortunately, I believe you will have to duplicate the markup for those conditions individually. Let me suggest the following markup:

<!-- th:block rather than unneeded div -->
<th:block th:switch="${status.value}">
    <div th:case="'COMPLETE'">
        <!-- print object is not active -->
    </div>
    <div th:case="'INVALID'">
        <!-- print object is not active -->
    </div>
    <div th:case="'NEW'">
        <!-- print object is new and active -->
    </div>
</th:block>

Alternatively you could resort to th:if which might actually work better in this case:

<div th:if="${status.value} eq 'COMPLETE' or ${status.value} eq 'INVALID'">
    <!-- print object is not active -->
</div>
<div th:if="${status.value} eq 'NEW'">
    <!-- print object is new and active -->
</div>

Or even more simply:

<div th:unless="${status.value} eq 'NEW'">
    <!-- print object is not active -->
</div>
<div th:if="${status.value} eq 'NEW'">
    <!-- print object is new and active -->
</div>
2
votes

I hit the same issue today, where the object used in my th:switch is a Java enum. I eventually figured out that this was a Java equals() versus == issue. In the th:switch I have an enum object, but in my th:case I have a String object. I solved it by using a Thymeleaf string function to convert the enum object to a string, and then everything works.

  <div th:switch="${#strings.toString(datafile.status)}">
      <td th:case="'SUCCESS'" class="table-success">SUCCESS</td>
      <td th:case="'FAILED'" class="table-danger">FAILED</td>
      <!-- default case -->
      <td th:case="*" th:text="${#strings.toString(datafile.status)}" class="table-secondary">xxx</td>
  </div>

In my example above I'm using the switch to conditionally apply a Bootstrap style to a table cell.


An alternate solution is to perform the logic in Java code and expose the output value as an object property, then just reference that property in the Thymeleaf template. Something like this:

public String getBootstrapTableRowClassForStatus() {
    Objects.requireNonNull(status);
    switch (status) {
        case SUCCESS:
            return "table-success";
        case FAILED:
            return "table-danger";
        case PROCESSING:
            return "table-info";
        default:
            return "table-secondary";
    }
}

and then I use Thymeleaf th:class:

<tr th:class="${datafile.bootstrapTableRowClassForStatus}">

On my page, this will apply a Bootstrap color style to my table row based on the value of my Status enum in the Java object.

0
votes

As pens-fan-69 answered this type of switch

switch(status.value){
  case 'COMPLETE':
  case 'INVALID':
     break;
  case 'NEW':
     break;
}

can be only translated in Thymeleaf to following switch statement (if you want to keep switch-case syntax) and not reply it with if-else

<th:block th:switch="${status.value}">
    <div th:case="'COMPLETE'">
        <!-- print object is not active -->
    </div>
    <div th:case="'INVALID'">
        <!-- print object is not active -->
    </div>
    <div th:case="'NEW'">
        <!-- print object is new and active -->
    </div>
</th:block>

I was facing an issue that in such cases I had to duplicate a lot of HTML code (in this case for COMPLETE and INVALID). This can be mitigated by using th:fragment and reusing them like this:

<th:block th:fragment="complete-or-invalid-case">
  <!-- your HTML for complete or invalid case -->
</th:block>

<th:block th:fragment="new-case">
  <!-- your HTML for new case -->
</th:block>

Then in switch you can reuse these blocks which reduces code duplicity

<th:block th:switch="${status.value}">
    <div th:case="'COMPLETE'" th:include="this :: complete-or-invalid-case"></div>
    <div th:case="'INVALID'" th:include="this :: complete-or-invalid-case"></div>
    <div th:case="'NEW'" th:include="this :: new-case"></div>
</th:block>