Search code examples
thymeleaf

Define and insert Thymeleaf fragments in TEXT templates


I'm trying to create email templates in both plain text and HTML with Thymeleaf. Because I don't want to duplicate the common parts I want to define these parts separately and insert them into the more specific templates.

It works for HTML, but for plain text variables in the common parts are not replaced:

HTML

  • common.html

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        </head>
        <body>
            <div th:fragment="header">
                <p>
                    Hello, [( ${name} )]
                </p>
            </div>
            <div th:fragment="footer">
                <p>
                    Bye.
                </p>
            </div>
        </body>
    </html>
    
  • specific.html

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        </head>
        <body>
            <div th:replace="html/common::header"></div>
            <p>
                <a th:href="${myLink}">[( ${myLink} )]</a>
            </p>
            <div th:replace="html/common::footer"></div>
        </body>
    </html>
    

Plain text

  • header.txt

    Hello ${name}
    
  • footer.txt

    Bye
    
  • specific.txt

    [( ~{text/header} )]
    [( ${myLink} )]
    [( ~{text/footer} )]
    

Result

It all works well for HTML but for the plain text version the ${name} variable from the inserted header.txt template is not replaced:

Hello, [#th:block th:utext="${name}"][/th:block]

    http://example.com

Bye.

The HTML result looks correct:

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
        <div>
            <p>
                Hello, name-value
            </p>
        </div>
        <p>
            <a href="http://example.com">http://example.com</a>
        </p>
        <div>
            <p>
                Bye.
            </p>
        </div>
    </body>
</html>

My questions

  • Is there an elegant solution for the plain text version?
  • Is there a way to define and use fragments also for textual Thymeleaf templates?
  • Any general recommendations, as I'm only starting to use Thymeleaf?

Solution

  • Variables in the Plain Text Version

    For the plain text issue, you can use the [#th:block] syntax.

    Specifically, instead of using this in your specific.txt:

    [( ~{text/header} )]
    

    you can use this:

    [#th:block th:replace="text/header"][/th:block]
    

    Also, in the header.txt file, instead of using this:

    Hello ${name}
    

    you need to use this:

    Hello [( ${name} )]
    

    This is expression inlining - which you have already used - and is presented here, for reference.

    Some additional examples of the [#th:block] syntax are presented here.

    Defining and Using Fragments for Text

    You might think that the [#th:block] syntax would now allow us to use fragments, in a similar way to the HTML approach. For example, something like this:

    DOES NOT WORK:
    [#th:block th:replace="text/common :: header"][/th:block]
    

    together with a common.txt fragment like this:

    ALSO DOES NOT WORK:
    [#th:block th:fragment="header"]
    Hello, [( ${name} )]
    [/th:block]
    

    If you try this, you will get the following error:

    java.lang.IllegalArgumentException: Template selectors cannot be specified for a template using a TEXT template mode: template insertion operations must be always performed on whole template files, not fragments

    General Comments

    The only other thing I would mention here, if you have not already seen or used it, is parameterized fragments. They can make HTML fragments more flexible and re-usable.