3

Ok so I'm using thymeleaf page layouts.

It seems to be working fine, however, I want to know how would I configure the <title></title> property in the <head></head> of a page that uses a fragment? Currently, everything in head is overriden by the header fragment (which can be seen below).

This is my: resources/templates/fragments/header.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head th:fragment="header">
    <link href="../../static/css/bootstrap.min.css" th:href="@{css/bootstrap.min.css}" rel="stylesheet" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js" th:src="@{js/bootstrap.min.js}"></script>
</head>
<body></body>
</html>

This is my index page (I want the title tag to display):

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">

<head th:include="fragments/header :: header">
    <title>THIS DOES NOT SHOW</title>
</head>
<body>
    <div class="container">
        <div th:include="fragments/headerNavbar :: headerNavbar"></div>

        <h1>hey</h1>
        <div class="btn btn-success">button</div>
        <button class="btn btn-success">another</button>       
    </div>

This is the actual HTML output:

 <head>
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <script src="js/bootstrap.min.js"></script>
</head>

I get what's going on. The header fragment is being loaded (included) into the head of my index page, thus its overriding all properties.

My problem is that I don't know how to have a header fragment AND have a file use that fragment while specifying a property, like a title.

This is what I have tried in index file:

<head>
    <div th:include="fragments/header :: header"></div>
    <title>THIS DOES NOT SHOW</title>
</head>

But it really messes up my HTML. There's got to a be a simple solution?

benscabbia
  • 17,592
  • 13
  • 51
  • 62
  • Probably something that I too wanted to ask. – Prashant Mar 29 '16 at 13:15
  • @Prashant it's a weird one tbh! Doesn't really seem to be an obvious solution. There are possible ways round it, maybe with Jquery, but that is clunky... – benscabbia Mar 29 '16 at 13:22
  • Yes agreed! They could only be considered as workarounds and not solutions especially when it is claimed that thymeleaf is a natural templating engine. Would really like to know if we are missing something here.. – Prashant Mar 29 '16 at 13:29
  • @Prashant found a solution! Check out my answer in a minute – benscabbia Mar 29 '16 at 13:37
  • Looks like a solution as is ment for embeding objects in html. Thanks for the solution – Prashant Mar 30 '16 at 04:41

2 Answers2

5

Ok I found a way round it using this solution. I still think it's clunky, and I still think there must be a better way but for my needs this is sufficient (please do share a solution if you have one):

The magic is with the th:remove="tag", at runtime, the object tag is removed and displays the fragment appropriately:

<head>
    <title>THIS DOES SHOW</title>
    <object th:include="fragments/header :: header" th:remove="tag"><object> 
</head>

So now the output is:

<head>
    <title>THIS DOES SHOW</title>
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <script src="js/bootstrap.min.js"></script>
</head>
Community
  • 1
  • 1
benscabbia
  • 17,592
  • 13
  • 51
  • 62
  • Good solution but you have an error in your code, you are not closing the – Ema.jar Dec 29 '16 at 17:13
0

You could use the Thymeleaf Layout Dialect, which provides support for hierarchical layout through new attributes. Those attributes are similar to the Thymeleaf ones, but providing additional features.

For example, it allows to mix the contents of the layout head section with the contents of the head section from the page providing the body section.

You'll have to create a template to be used as the layout for your pages. Based on your example, you would have something like this:

File resources/templates/layouts/layout.html

<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
  <head>
    <link href="../../static/css/bootstrap.min.css" th:href="@{css/bootstrap.min.css}" rel="stylesheet" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js" th:src="@{js/bootstrap.min.js}"></script>
    <title layout:title-pattern="$DECORATOR_TITLE - $CONTENT_TITLE">My app</title>
  </head>
  <body>
    <section layout:fragment="content">
        <!-- Content replaced by each page's content fragment -->
        <p>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          Praesent scelerisque neque neque, ac elementum quam dignissim interdum.
        </p>
    </section>
  </body>
</html>

File resources/templates/index.html

<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" 
      layout:decorator="layouts/layout">
<head>
    <title>Home page</title>
</head>
<body>
    <div layout:fragment="content">
        <h1>hey</h1>
        <div class="btn btn-success">button</div>
        <button class="btn btn-success">another</button>       
    </div>
</body>
</html>

This way the resulting page will have the following head section:

<head>
  <link href="../../static/css/bootstrap.min.css" th:href="@{css/bootstrap.min.css}" rel="stylesheet" />
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js" th:src="@{js/bootstrap.min.js}"></script>
  <title>My app - Home page</title>
</head>

Note the layout:title-pattern attribute that allows to set a pattern to generate the final title. The layout dialect will merge by default any script or stylesheet included also in the page's head section. I haven't included any fragment for the header, footer, menu, etc. for the sake of simplicity, but it is supported as well.

Take a look at the project's page to see which dependency you have to add to your project and the required configuration. If you are using boot, this dialect is already supported by the Thymeleaf starter, so you'll only have to add the dependency to your project and it will be autoconfigured.

EDIT: I have corrected a typo in the layout example, was using layout:include instead of layout:fragment.

Cèsar
  • 1,206
  • 11
  • 22
  • I've accepted it since I prefer this solution to my original. I love hierarchical layouts so I will need to look further into it :) – benscabbia Mar 30 '16 at 12:06
  • had to make a small change to the example due to exception. I change the layout: `layout:include="content"` to `layout:include="::content"` – benscabbia Mar 30 '16 at 12:14
  • Sorry, my fault, it should be changed to _layout:fragment="content"_ instead. This is the atribute to use to include the page content. Corrected in the answer. – Cèsar Mar 31 '16 at 08:00