Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

th:replace doesn't work well with th:with in Thymeleaf

According to Thymeleaf docs

Fragments can include any th:* attributes. These attributes will be evaluated once the fragment is included into the target template (the one with the th:insert/th:replace attribute), and they will be able to reference any context variables defined in this target template.

My Fragment

<div th:fragment="link">
    <a th:href="@{${url}}"><span th:inline="text">[[${text}]]</span></a>
</div>

This is how I include it.

<div th:replace="fragments/common :: link" th:with="url='www.google.com', text='Click Me'"></div>

The html i get

<a href="">
    <span>null</span>
</a>

However the same works fine with th:include and gives me following HTML.

<a href="www.google.com">
    <span>Click Me</span>
</a>

Why th:replace doesn't work while th:inlcude works fine?

NOTE: th:insert is out of scope because i am using Thymeleaf v2.1.5

like image 672
Abdullah Khan Avatar asked Jun 24 '26 05:06

Abdullah Khan


1 Answers

The reason is that th:replace actually removes current tag so you lose every attribute you had there, but get all the attributes from fragment. And in your case this means that you never defined any th:with variable in the scope.th:include works the opposite way. You loose fragment tag, but keep everything defined in layout.

Consider this fragment:

<fragmenttag th:fragment="link" style="background-color: red">...</fragmenttag>

And layout:

<layouttag th:include="fragments/common :: link" style="font-size: 250%;"/>
<layouttag th:replace="fragments/common :: link" style="font-size: 250%;"/>

The result is:

<layouttag style="font-size: 250%;">Some Text</layouttag>
<fragmenttag style="background-color: red">Some Text</fragmenttag>

If you want to use th:replace, because you have some important attributes in fragment, you can define everything you need in some parent tag in layout.

<body th:with="url='www.google.com', text='Click Me'">
    <div th:replace="fragments/common :: link" ></div>
</body>

You are referencing documentation in your post:

Fragments can include any th:* attributes. These attributes will be evaluated once the fragment is included into the target template (the one with the th:insert/th:replace attribute), and they will be able to reference any context variables defined in this target template.

And i don't see any contradiction here, because this part of the documentation is about th:* attributes inside a fragment.

Fragments (th:fragment part) can include any th:* attributes.

And in your question you are talking about loosing th:* attributes defined in target template. But anyway, this part is quite strait that you perform inclusion logic first

These attributes will be evaluated once the fragment is included

There is nothing here that lets you assume that you will get everything you defined in target template or fragments main tag, because both of them can be replaced depending on witch inclusion strategy you are going to use (th:insert/th:replace).

So you defined th:with="url='www.google.com', text='Click Me'" attribute, but it was never included in the end result template because you selected th:replace inclusion strategy, so th:with attribute was never evaluated and you got no url and text variables in scope. No contradiction here.

like image 92
varren Avatar answered Jun 25 '26 20:06

varren