The below should set h1 to HOWDY as the locale is showing en_GB, but it doesn't fall into the case?
Am I doing something wrong here? Thanks
<h1 th:text="${#locale}"></h1>
<div th:switch="${#locale}">
<h1 th:case="'en_GB'">HOWDY</h1>
</div>
When you use Thymeleaf's #locale, you are using a reference to a Java Locale object.
What Works?
The following works the way you expect, because it has already evaluated the Java locale object to its string representation, before evaluating each case statement:
<div th:switch="${#locale.toString()}">
<h1 th:case="'en_GB'">HOWDY</h1>
</div>
The following also works:
<div th:switch="__${#locale}__">
<h1 th:case="'en_GB'">HOWDY</h1>
</div>
In this case, it works because you are using the Thymeleaf preprocessor syntax __${...}__ to force Thymeleaf to evaluate #locale (to its string representation) before it evaluates the remainder of the switch statement.
Additional Explanation
Because Thymeleaf's #locale represents a Java Locale object, you can use any of Locales suitable fields and methods, such as :
<div th:text="${#locale.country}"></div> <!-- a field -->
<div th:text="${#locale.toLanguageTag()}"></div> <!-- a method -->
This is why only using ${#locale} in the Thymeleaf switch statement does not match the 'en_GB' string literal that you are expecting it to match: Thymeleaf is comparing an object to a string.
When you use this...
<div th:text="${#locale}"></div>
...you are again accessing the object itself. But in this case the object will use its toString() method when being rendered by Thymeleaf, before it is displayed - giving you your en_GB displayed value.
However, when you use this...
<div th:switch="${#locale}">
<h1 th:case="'en_GB'">HOWDY</h1>
</div>
...Thymeleaf is using the locale object in the switch statement, not its string representation.
Related
How can we write HTML and string literals at a same time in Thymeleaf ?
<div class="details"><span class="Section" th:utext="'Sec' <br> ${wind.sec}"></span><span class="Axiom" th:utext="'Axiom' <br> ${wind.axiom}"></span></div>
This throws error
Cannot execute GREATER THAN from Expression "('Sec' < br) > ${wind.sec}". Left is "true", right is "Great"
You can use the following = which uses + for string concatenation:
<div class="details">
<span class="Section" th:utext="'Sec<br>' + ${wind.sec}"></span>
<span class="Axiom" th:utext="'Axiom<br>' + ${wind.axiom}"></span>
</div>
The <br> is just part of the text literal in this case - because you are using th:utext.
However, using unescaped values such as ${wind.sec} is unsafe and is not recommended. There could be harmful values in that variable - especially if the variable holds data provided by end users.
So, unless the following structure change poses a problem, I would recommend something like this:
<div class="details">
<span class="Section" th:utext="'Sec<br>'"></span>
<span class="Section" th:text="${wind.sec}"></span>
<span class="Axiom" th:utext="'Axiom<br>'"></span>
<span class="Axiom" th:text="${wind.axiom}"></span>
</div>
Here we have separated the true text literals (which can use th:utext) from the variables (which should use th:text). Now, any HTML which may have found its way into your ${...} variables will be escaped, rendering it harmless.
I'm trying to iterate over a list and pass the current iteration and another model variable to a fragment, but the "other" model variable is always null.
<div th:each="place : ${results.placeResults}" class="col-sm-6 col-xl-4 mb-5">
<div th:replace="fragments/placecard :: placecard" th:with="place=${place},res=${results}"/>
</div> <!-- end for each-->
In the fragment ${res} is always blank.
I figured it out, the th:replace basically makes the th:with have no effect. I changed the code to use th:include and things look better.
To make this simple, let's assume we have a template html file(test.htm) like this:
<div th:fragment="test">
this is a test fragment
</div>
<div th:include=":: test"> <!-- we'll change this line later -->
this is a placeholder
</div>
And the following controller is used to return test.htm:
#Controller
public class HomeController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView get(ModelAndView mav) {
mav.setViewName("/test.htm");
mav.addObject("fragmentName", ":: test"); // we'll use this later
return mav;
}
}
In this case, we can get the following result if we access home index:
this is a test fragment
this is a test fragment
But if we use a variable fragmentName to set th:include value like this:
<div th:fragment="test">
this is a test fragment
</div>
<div th:include="${fragmentName}"> <!-- modified to use a variable value -->
this is a placeholder
</div>
Thymeleaf complains that this template ":: test" cannot be resolved:
There was an unexpected error (type=Internal Server Error, status=500).
Error resolving template [:: test], template might not exist or might not be accessible by any of the configured Template Resolvers (template: "/test.htm" - line 5, col 6)
Here comes the question: is there a way to set the th:include value using a variable?
You can use the Thymeleaf expression preprocessing:
<div th:include="__${fragmentName}__">
this is a placeholder
</div>
Basically you instructed thymeleaf to preprocess first __${fragmentName}__ and after resolving the value to use it in the normal processing phase when evaluates the th:include as if it was a static value th:include=":: test"
There is the structure like:
<div class="parent">
<div>
<div class="fieldRow">...</div>
</div>
<div>
<div class="fieldRow">
<div class="CheckBox">
</div>
</div>
<div>
<div class="fieldRow">...</div>
</div>
<div>
<div class="fieldRow">...</div>
</div>
</div>
In my script I am writing a loop for each of the 4 div's under div[#class='parent'] and aiming to click the checkbox if there is, i.e.
members = page.all(:xpath, '//div[#class='parent'])
members.each do |a|
if **page.has_xpath?(a).find(:xpath, "div[#class='fieldRow']/div[#class='CheckBox']")**
a.find(:xpath, "div[#class='fieldRow']/div[#class='CheckBox']").click
end
end
However I can't look for the correct usage of has_xpath? with xpath including variable.
Please advice? Thank you!
has_xpath? takes an XPath expression (not an element) and returns a boolean (true/false) based on whether there are any elements that match that expression within the current scope - http://www.rubydoc.info/gems/capybara/Capybara/Node/Matchers#has_xpath%3F-instance_method. Since it returns true/false you can't then call find on it. For the example you posted there's no need for XPath or checking for the existence of the elements, just find all the matching elements and call click on them. Something like
page.all('div.parent div.fieldRow div.Checkbox').each { |cb| cb.click }
or
page.all('div.parent div.Checkbox').each { |cb| cb.click }
if the fieldRow class isn't something you really need to check.
Note: this assumes clicking the elements doesn't invalidate any of the other matched elements/change the page.
If you REALLY need to do it with the whole members and looping on them , using XPath, and checking for presence then it would be something like
members = page.all(:xpath, './/div[#class='parent'])
members.each do |a|
if a.has_xpath?(:xpath, ".//div[#class='fieldRow']/div[#class='CheckBox']")
a.find(:xpath, ".//div[#class='fieldRow']/div[#class='CheckBox']").click
end
end
Note: the .// at the beginning of the XPath expressions is needed for scoping to work correctly - see https://github.com/teamcapybara/capybara#beware-the-xpath--trap - which is an issue using CSS selectors doesn't have, so you should really prefer CSS selectors whenever possible.
I've used <g:set> tag like this:
<g:set var="extraStyle" value="style='min-width:120px;'"/>
and used the extraStyle variable like this:
<div class="myClass" ${extraStyle}> ${myValue}</div>
And it should be rendered as:
<div class="myClass" style="min-width:120px;"> XYZ </div>
But, I am getting this instead:
<div class="myClass" style="'min-width:120px;'"> XYZ </div>
Due to which, min-width style is not being applied. What am I doing wrong here?
Grails version: 3.1.6
You could try just setting the style value e.g.
<g:set var="extraStyle" value="min-width:120px;"/>
<div class="myClass" style="${extraStyle}"> ${myValue}</div>
I think Mike's answer is correct, and although I do not know the context of your project I think it might in the long run be better to add a class dynamically to the element.
Something like
<div class="myClass ${extraClass}">...</div>