Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Thymeleaf + spring dynamic replace

Is it possible to create a dynamic replace in Thymeleaf?

I have the following controller:

@Controller
public class LoginController {

    @RequestMapping("/login")
    public String getLogin(Model model){
        model.addAttribute("template","login");
        return "index";
    }
}

And the following view:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" >
<head></head>
<body>

<div th:replace="fragments/${template} :: ${template}"></div>

</body>
</html>

And i'm getting the following error:

Error resolving template "fragments/${template}", template might not exist or might not be accessible by any of the configured Template Resolvers

UPDATE

I tried to preprocess my variables like this:

<div th:replace="fragments/${__#{${template}}__} :: ${__#{${template}}__}"></div>

How ever now ${template} is getting replaced with login i have the following error now:

Exception evaluating SpringEL expression: "??login_en_US??"
like image 849
Wermerb Avatar asked Aug 01 '15 11:08

Wermerb


4 Answers

In Thymeleaf 3.0, the following solution has worked for me:

<div th:replace="('fragments/' + ${template}) :: (${template})">

(Note however, that I use it with fixed name of the fragment and dynamic name of the template, so the parantheses around :: (${template}) might be optional.)

The solution is inspired by documentation for Thymeleaf in https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#fragment-specification-syntax

Both templatename and selector in the above examples can be fully-featured expressions (even conditionals!) like: <div th:insert="footer :: (${user.isAdmin}? #{footer.admin} : #{footer.normaluser})"></div> Note again how the surrounding ~{...} envelope is optional in th:insert/th:replace

like image 193
trohr Avatar answered Nov 03 '22 22:11

trohr


Although Joe Essey's solution is working as well i solved with following code:

<div th:replace="@{'fragments/' + ${template}} :: ${template}"></div>
like image 25
Wermerb Avatar answered Nov 03 '22 21:11

Wermerb


I believe the appropriate method to manage this behavior in thymeleaf is to use layout:fragment tags. Please correct me if I'm wrong. Here is a simple example of my layout page, and the login page which is 'dynamically' loaded:

layout.html

<html xmlns:layout="http://www.w3.org/1999/xhtml" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <title layout:title-pattern="$DECORATOR_TITLE - $CONTENT_TITLE">Layout</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
</head>
<body>
<div>
    <div class="app-container">
        <div th:fragment="content">
        </div>
    </div>
</div>
<div th:fragment="script"></div>
</body>
</html>

Then, when login gets loaded, it replaces the th:fragment div with the associated div in the html view which matches the string returned by the controller method, in this case login.html:

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.w3.org/1999/xhtml"
      layout:decorator="layout">
<head>
    <title>Login</title>
</head>
<body>
<div th:fragment="content">
    <form th:action="@{/login}" method="post">
        <div><label> User Name : <input type="text" name="username"/> </label></div>
        <div><label> Password: <input type="password" name="password"/> </label></div>
        <div><input type="submit" value="Sign In"/></div>
    </form>
</div>
</body>
</html>

Now, if you want to load another fragment conditionally, the approach I take is to add replace tags with th:if cases. Here's an example of a Form that displays different questions based on an attribute of the current user:

<div th:if="${foo.type)} == 'type_1'">
    <div th:replace="fragments/custom-questions :: type-1-checkboxes"></div>
</div>
<div th:if="${foo.type} == 'type_2'">
    <div th:replace="fragments/custom-questions :: type-2-checkboxes"></div>
</div>

Then the associated div gets loaded from the file custom-questions.html:

<div th:fragment="type-1-checkboxes">
  //stuff
</div>

<div th:fragment="type-2-checkboxes">
  //stuff
</div>
like image 5
Joe Essey Avatar answered Nov 03 '22 21:11

Joe Essey


I am just encountering this issue (this is my first time with thymeleaf/spring). This is what solved it for me:

<div class="col-md-12" th:include="__${template}__ :: body" ...
like image 3
Ifelere Bolaji Avatar answered Nov 03 '22 22:11

Ifelere Bolaji