Romain Linsolas
Romain Linsolas

Reputation: 81617

Thymeleaf templates - Is there a way to decorate a template instead of including a template fragment?

I am working with Thymeleaf for the first time, and I need a clarification about the templates. If I correctly understand the documentation, I can include a template - or just a fragment of it - in my page. So for example, I can write something like that:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
    <head th:include="template/layout :: header">
    </head>
    <body>
        Hello world
        <div th:include="template/layout :: footer"></div>
    </body>
</html>

But what I want is in fact the opposite way of using the template : instead of including template fragment in the page, I want to include the page inside my template, something like that:

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    ...
</head>
<body>

    <div id="my-template-header">...</div>

    <div id="the-content">
        <!-- include here the content of the current page visited by the user -->
        ???
    </div>

    <div id="my-template-footer">...</div>
</body>

In others words, is there a way to have an equivalent of the Sitemesh decorators tags in Thymeleaf?

Thanks

Upvotes: 40

Views: 33894

Answers (4)

&#201;ricoGR
&#201;ricoGR

Reputation: 710

with Thymeleaf 2.1, you can write something like that:

Create the template (for ex. templates/layout.html), and add the th:fragment="page" information in html tag and define the content area with th:include="this :: content" information:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      th:fragment="page">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Test</title>
    </head>
    <body>
        layout page
        <div th:include="this :: content"/>
        layout footer
    </body>
</html>

Now create the page that will include this template adding th:include="templates/layout :: page" in html tag and put your main content inside a div with th:fragment="content"

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      th:include="templates/layout :: page">
    <head>
        <title></title>
    </head>
    <body>
        <div th:fragment="content">
            my page content
        </div>
    </body>
</html>

In layout page you can use this (th:include="this :: content") or suppress this option (th:include=":: content"). It seems like jsf facelets I think.

Upvotes: 69

mrclrchtr
mrclrchtr

Reputation: 1093

You need

<dependency>
    <groupId>nz.net.ultraq.thymeleaf</groupId>
    <artifactId>thymeleaf-layout-dialect</artifactId>
    <version>1.2.2</version>
</dependency>

and add this to your SpringTemplateEngine:

@Bean
@Description("Thymeleaf template engine with Spring integration")
public SpringTemplateEngine templateEngine() {
    SpringTemplateEngine templateEngine = new SpringTemplateEngine();
    templateEngine.setTemplateResolver(templateResolver());
    templateEngine.addDialect(new LayoutDialect());

    return templateEngine;
}

If now create a folder named template in your views folder.

views/home.html

<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
  layout:decorator="template/layout">
 <body>
  <div layout:fragment="content">
    Hello world
  </div>
 </body>
</html>

views/layout/layout.html

<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
        xmlns:th="http://www.thymeleaf.org"
        xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
    <head>
      </head>
        <body>
          <div id="content" layout:fragment="content">
          </div>
</body>
</html>

Upvotes: 2

Romain Linsolas
Romain Linsolas

Reputation: 81617

Ok, as stated by Sotirios Delimanolis, Thymeleaf does not support that way of using template, or should I say "Hierarchical layouts", as explained by Daniel Fernandez in this thread.

As sitemesh and Thymeleaf are compatible, it seems that I have to use both solutions. Too bad.

Edit: As suggested by DennisJaamann in a comment, I finally used Thymeleaf Layout Dialect, a view dialect that provides the feature I was looking for.

The working code:

First I add the LayoutDialect class:

@Bean
public ServletContextTemplateResolver templateResolver() {
    ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
    resolver.setPrefix("/WEB-INF/views/");
    resolver.setSuffix(".html");
    //NB, selecting HTML5 as the template mode.
    resolver.setTemplateMode("HTML5");
    resolver.setCacheable(false);
    return resolver;
}

Then, I create the template (for ex. templates/layout.html), and add the layout:fragment information where I want to put the content of the current page:

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    ...
</head>
<body>
    <div id="my-template-header">...</div>

    <div id="the-content" layout:fragment="content">
        <!-- include here the content of the current page visited by the user -->
    </div>

    <div id="my-template-footer">...</div>
</body>

and the page will refers to the template with the attribute layout:decorator:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorator="templates/layout">
<body>

    <div layout:fragment="content">
        Hello world
    </div>

</body>
</html>

Upvotes: 20

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279940

As far as I know, you can't. A possible solution would be to create a ViewResolver that always forwards to your decorator file, but at the same time put Model attributes that would have the actual path to the fragment you want to include.

Upvotes: 0

Related Questions