Thymeleaf - pass variables to fragments - thymeleaf

I have following template with fragment where I am passing variable:
<div th:fragment="main">
<span>
<th:block th:include=" :: inner(${item})" />
</span>
<a>
<th:block th:include=" :: inner(${item})" />
</a>
</div>
<th:block th:fragment="inner(item)">
[[${item.name}]]
</th:block>
If I try to render it I get an error saying:
Property or field 'name' cannot be found on null
If I render it like this
<div th:fragment="main">
<span>
<th:block th:include=" :: inner(${item})" />
[[${item.name}]]
</span>
<a>
<th:block th:include=" :: inner(${item})" />
</a>
</div>
What am I doing wrong when assigning variable to fragment?

Figured it out.
The reason of error was that the 'inner' fragment was rendered during the render along with main fragment. So along with inclusions if was rendered by itself. And this last render spoiled it.
Possible solutions are to:
move inner fragment to another file
add th:if="false" to 'inner' fragment to avoid self rendering (inclusions will continue to work).

Related

Do thymeleaf fragments need to exist inside a well-formed HTML file?

I'm creating a couple of thymeleaf fragments to use in other pages, and I was told they need to be inside a well-formed html page (with HTML, HEAD, BODY tags etc). Is this the case?
The fragments are only going to be used with th:include/th:replace in other places.
A fragment just needs to be a well formed html. You can start with a div
For e.g
<div th:fragment="formField (field, value, size)">
<div>
<label th:for="${#strings.toLowerCase(field)}"> <span
th:text="${field}">Field</span>
</label>
</div>
<div>
<input type="text" th:id="${#strings.toLowerCase(field)}"
th:name="${#strings.toLowerCase(field)}" th:value="${value}"
th:size="${size}">
</div>
Which you then include somewhere else
<body>
<header th:insert="fragments/general.html :: header"> </header>
<div th:replace="fragments/forms.html
:: formField(field='Name', value='John Doe',size='40')">
</div>
<div th:replace="fragments/general.html :: footer"></div>
I took these examples from here: https://www.baeldung.com/spring-thymeleaf-fragments

Thymeleaf th:each attribute failed on form element

I find Thymeleaf th:each is not working properly on <form> element when ${items} is empty.
<form th:each="item,iterStat : ${items}" >
<span th:text="${iterStat.size}"> Hello </span>
</form>
org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'size' cannot be found on null
But, it is ok on <div> element
<div th:each="item,iterStat : ${items}" >
<span th:text="${iterStat.size}"> Hello </span>
</div>
The Thymeleaf is included in Spring Boot v1.5.4.RELEASE
Why?

How to pass a fragment to a message expression in Thymeleaf

Is it possible to pass a fragment to a message expression in Thymeleaf? I want to re-use fragments for creating links in messages.
My fragment looks something like this:
<div th:fragment="link(url, text)" th:remove="tag">
<a th:href="#{${url}}"><span>${text}</span></a>
</div>
and I have a message like that:
home.welcome=Hello User! See new content at {0}.
Now I want to pass the evaluated fragment to the message expression (pseudo-code):
<p th:utext="#{home.welcome(${link:: link(url='myUrl', text='myText')})}"></p>
The resulting HTML should look like this:
<p>
Hello User! See new content at <span>myText</span>.
</p>
I discovered Fragment expressions introduced in Thymeleaf 3 but I'm not sure if they are the way to go.
You can try th:with.
Call fragments like so
<div th:include="fragments/common :: link" th:with="url='www.google.com', text='Click Me'"></div>
This is your fragment
<div th:fragment="link" th:remove="tag">
<a th:href="#{${url}}"><span th:inline="text">[[${text}]]</span></a>
</div>
This is the HTML you will get
<div>
<a href="www.google.com">
<span>Click Me</span>
</a>
</div>

Conditional Thymeleaf fragments

I would like to create a list of up to three items using a template fragment. Three spaces for items will always be displayed regardless of whether or not there is an item, so it looks something like this.
<div>
<div th:if="${#lists.size(block.children) > 0}"
th:insert="code-block :: block(${block.children[0]})"
th:remove="tag">
</div>
</div>;
<div>
<div th:if="${#lists.size(block.children) > 1}"
th:insert="code-block :: block(${block.children[1]})"
th:remove="tag">
</div>
</div>;
<div>
<div th:if="${#lists.size(block.children) > 2}"
th:insert="code-block :: block(${block.children[2]})"
th:remove="tag">
</div>
</div>
However, even though the th:if statement evaluates as false with an empty list, it still attempts to execute the `th:include statement, giving me the following error:
Caused by: org.springframework.expression.spel.SpelEvaluationException:
EL1025E:(pos 14): The collection has '0' elements, index '0' is invalid
How can I get the if statement to take precedence over the fragment execution?
Yeah, unfortunately since include has precedence over if, you're going to have to move the if higher up. The simplest way would be to put it in a th:block, like this:
<div>
<th:block th:if="${#lists.size(block.children) > 0}">
<div th:insert="code-block :: block(${block.children[0]})" th:remove="tag" />
</th:block>
</div>;
<div>
<th:block th:if="${#lists.size(block.children) > 1}">
<div th:insert="code-block :: block(${block.children[1]})" th:remove="tag" />
</th:block>
</div>;
<div>
<th:block th:if="${#lists.size(block.children) > 2}">
<div th:insert="code-block :: block(${block.children[2]})" th:remove="tag" />
</th:block>
</div>
You could also probably simplify your code to look something more like this:
<th:block th:each="i: ${#numbers.sequence(0, 2)}">
<th:block th:if="${#lists.size(block.children) > i}">
<div th:insert="code-block :: block(${block.children[i]})" th:remove="tag" />
</th:block>
<th:block th:unless="${i == 2}">;</th:block>
</th:block>

Can “field-with-errors” be attached to the parent of the input tag that raises the error?

So I have an input element like this. The wrapping element is about, you know, a visual thing.
<div class="input-wrap">
<input class="blah-blah" />
</div>
When the <input> contains the error, it'll be like this:
<div class="input-wrap">
<div class="field-with-errors">
<input class="blah-blah" />
</div>
</div>
But what I want to do is:
<div class="input-wrap field-with-errors">
<input class="blah-blah" />
</div>
I found this page, it's very close to my question
Rails 3: "field-with-errors" wrapper changes the page appearance. How to avoid this?
Now I know I can throw
config.action_view.field_error_proc = Proc.new { |html_tag, instance|
"#{html_tag}".html_safe
}
to avoid making a wrapping tag around the <input> tag that has an error on. But what I really wanna do is, again, adding "field-with-errors" class on the direct parent of the <input> tag. Can I do that? Does ActionView hold the tree structure of DOM Nodes?
You can put the code for handling errors wherever you like, just call it as a block on the instance variable, for example
if #instance.errors.any?
<div class="field with errors">
#instance.errors.full_messages.each do |msg|
<p><%= msg %></p>
end
end
If you user this a lot, it's good to pull it out into a helper and pass in the instance variable as a parameter.

Resources