Issue
I'm using preprocessing in thymeleaf to append a class in <i>
based on whether the user solved a challenge or not. I'm using a method isSolvedBy
that takes a username string to check if the user solved the challenge. In the template, the username string is passed via calling another method getUsername
that returns the username of the logged-in user. When calling getUsername()
method it passes a null value to the caller function.
Controller snippet:
model.addAttribute("user", user.get());
model.addAttribute("score", user.get().getScore());
model.addAttribute("rank", user.get().getUserRank());
model.addAttribute("challenges", challengeList);
String challengesNumber = Integer.toString(challengeList.size());
model.addAttribute("challengesNumber", challengesNumber);
return "challenges";
challenges view snippet:
<a th:each="challenge: ${challenges}" th:href="@{/challenge(id=${challenge.challengeId})}" class="challenge-card white-text" style="text-decoration: none">
<h3 style="margin-top: 5px;" th:text="${challenge.challengeTitle}" class="golden-text"></h3>
<i th:classappend="${challenge.isSolvedBy(__${user.getUsername()}__)}"></i> <!-- here is the preprocessing -->
<div>
<h4 class="card-info">Points: <span class="golden-text" th:text="${challenge.challengeScore}"></span></h4>
<h4 class="card-info">Level: <span class="" th:text="${challenge.level}"></span></h4>
</div>
</a>
This is the challenge.isSolvedBy method:
public String isSolvedBy(String username)
{
System.out.println(username); // this prints null
return "solved-challenge"; // temporary return value
}
This user.getUsername() method:
public String getUsername() {
System.out.println(username); // this prints the username value when called by thymeleaf
return username;
}
When the controller is called everything works fine and the preprocessing statement "${challenge.isSolvedBy(__${user.getUsername()}__)}"
is executed without any errors but it passes a null value to challenge.isSolvedBy()
method even though user.getUsername()
will actually prints the actual username when it is called.
Here is the actual output:
2022-07-11 01:09:07.365 INFO 19416 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-07-11 01:09:07.365 INFO 19416 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2022-07-11 01:09:07.366 INFO 19416 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
yousef [printed from getUsername method]
null [printed from isSolvedBy method]
yousef [printed from getUsername method]
null [printed from isSolvedBy method]
I'm still learning spring MVC and Thymeleaf, so clearly I missed something but I can't figure what it is.
Anyone know what is the problem?
Thanks in advance.
Solution
It seems that thymeleaf did not passed the returned value from __${user.getUsername()}__
as a string. Single quote marks are needed because after the first pass in thymeleaf the result of ${challenge.isSolvedBy(__${user.getUsername()}__)}"
will be ${challenge.isSolvedBy(<result>)}
. I don't know why it passed it as null and did not throw an error. The correct expression should include '
around the expression that needs to be passed as a string, because when writing the preprocessing statement like this ${challenge.isSolvedBy('__${user.getUsername()}__')}
the result after the first thymeleaf pass will be ${challenge.isSolvedBy('<result')}
and thus thymeleaf will pass the result as a string to the method.
Answered By - Yousef Ali
Answer Checked By - Katrina (JavaFixing Volunteer)