Search code examples
springthymeleaf

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??"

Solution

  • 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>