Thymeleaf custom dialect - nested attributes - spring

I would like to ask if it is possible to have nested attributes in HTML tag which can reuse result of previous one. For example
<p custom:one="some text to process" custom:two="process result of custom:one">
where custom:one can be used standalone but custom:two have to be used with custom:one. The final result will be produced by custom:two

if I got you right, you can do it with local variables
You need to specify th:with to declare a variable.
Note that the declared variable is available within the element.
<div th:with="newValue = 'Hello ' + ${val}">
<span th:text="${val}">One</span>
<span th:text="${newValue}>Two</span>
</div>
Let me know if that's what you're looking for.

Related

pass thymeleaf variable as parameter in method

I concatenate the state and my stateCounter to one String. This is my value for my HashMap which stores all the States but I don't get it to to put this variable in my method.
<span th:with="stateName=${item.state} + ${item.stateCounter}"></span>
<td th:text="${{order.getStateRepository().get(${stateName})}}"></td>
Some notes:
(1) Using a <td> tag suggests you are also using a <table>. Having a <span> tag next to a <td> tag inside a table is not valid HTML (assuming this is what it looks like in your template - maybe this is just a copy/paste thing).
(2) If your order object has a stateRepository field, then you don't need to use a getter order.getStateRepository(). You can just use the field name order.stateRepository. You are already doing this with item.state, for example. Thymeleaf will figure out from the field name how to use the related getter - e.g. item.getState().
(3) The scope (availability/visibility) of a local variable, such as stateName in th:with="stateName=${item.state} is limited to the tag in which it is declared, and also to any sub-tags. You do not have any sub-tags in your <span> - therefore the variable is not visible anywhere outside of your span. So, it is not available inside the <td> tag.
(4) Do you need to use a local variable in your example?
Instead of using get(stateName), you can use the expression referred to by stateName directly:
get(item.state + item.stateCounter)
So overall, this should work (based on the assumptions above):
<td th:text="${order.stateRepository.get(item.state + item.stateCounter)}"></td>
Maybe you do need the local variable, of course. It may depend on the wider context of the Thymeleaf template.
You are trying to use local variable. It will work if you try as follows:
<div th:with="stateName=${item.state} + ${item.stateCounter}">
<td th:text="${order.getStateRepository().get(stateName)}"></td>
</div>

Thymeleaf Template not rendering param value inside th if condition

I am using thymeleaf template for my spring boot application. Here below the main page,
<div th:replace="content :: content"></div>
and inside content fragment,
<div th:fragment="content">
<h4 th:if="${param.val== 'abc'}">SOME-TEXT</h4> // not working
<h4 th:if="${param.val== 'abc'}" th:text="${param.val}"></h4> // not working
<h4 th:text="${param.val}"></h4> // working and value is abc
<h4 th:unless="${param.val== 'abc'}" th:text="${param.val}"></h4> // working - value in html text is abc
<h4 th:unless="${param.val== 'abc'}">SOME-TEXT</h4> // Working, value is SOME-TEXT
</div>
URL: domain/?val=abc
I want to display: SOME-TEXT in html if param.val == 'abc'.
Value 'abc' is coming inside th:text. But inside th:if failing.
Seems some hidden extra strings added to param.val?
Any suggestion?
The Thymeleaf function ${param.val} will return a request parameter called val. But this could be a multivalued object (e.g. an array) - for example consider this (which is a valid construction):
?val=abc&val=def
So to work with a single-valued string, you can do this:
<h4 th:if="${#strings.toString(param.val)} == 'abc'" th:text="'SOME-TEXT-2'">SOME-TEXT-1</h4>
This prints SOME-TEXT-2 in the web page.
Or you can use this:
<h4 th:if="${#strings.toString(param.val)} == 'abc'">SOME-TEXT-1</h4>
Which prints SOME-TEXT-1.
Just out of interest, if you used that first example val=abc&val=def, then you can see what happens with this:
<h4 th:text="${param.val}"></h4>
It prints an array:
[abc, def]
You may see something like this when processing a series of related checkboxes (just as one example).
Update:
For a null-check, using Thymeleaf, you can do this:
<h4 th:if="${param.val} != null and
${#strings.toString(param.val)} == 'abc'">SOME-TEXT-2</h4>
In this specific case, it isn't really needed, as you are not doing anything with the null value which might cause a problem.
It's more relevant if you are chaining values in objects foo.bar.baz - and you need to check if foo or bar are null to avoid a null pointer exception.
Bear in mind that Spring's expression language has the safe navigation operator, which can be very helpful in such cases: foo.?bar.?baz, allowing you to write more concise null handling than with Thymeleaf alone. But again, not relevant to your specific example from the question.

Page Object Gem. Making accessors for elements without ids

Lets say I have a simple page that has less IDs than I'd like for testing
<div class="__panel_body">
<div class="__panel_header">Real Estate Rating</div>
<div class="__panel_body">
<div class="__panel_header">Property Rating Info</div>
<a class="icon.edit"></a>
<a class="icon.edit"></a>
</div>
<div class="__panel_body">
<div class="__panel_header">General Risks</div>
<a class="icon.edit"></a>
<a class="icon.edit"></a>
</div>
<div class="__panel_body">
<div class="__panel_header">Amenities</div>
<a class="icon.edit"></a>
<a class="icon.edit"></a>
</div>
</div>
I'm using Jeff Morgan's Page Object gem and I want to make accessors for the edit links in any given section.
The challenge is that the panel headers differentiate what body I want to choose. Then I need to access the parent and get all links with class "icon.edit". Assume I can't change the HTML to solve this.
Here's a start
module RealEstateRatingPageFields
div(:general_risks_section, ....)
def general_risks_edit_links
general_risks_section_element.links(class: "icon.edit")
end
end
How do I get the general_risks_section accessor to work, though?
I want that to represent the parent div to the panel header with text 'General Risks'...
There are a number of ways to get the general risk section.
Using a Block
The accessors can take a block where you can more programatically describe how to locate the element. This allows you to locate a distinguishing element and then traverse the DOM to the element you actually want. In this case, you can locate the header with the matching text and navigate to its parent.
div(:general_risks_section) { div_element(class: '__panel_header', text: 'General Risks').parent }
Using XPath
While harder to read and write, you could also use an XPath locator. The concept and thought process is the same as using the block. The only benefit is that it reduces the number of element calls, which slightly improves performance.
div(:general_risks_section, xpath: './/div[#class="__panel_body"][./div[#class="__panel_header" and text() = "General Risks"]]')
The XPath is saying:
.//div # Find a div element that
[#class="__panel_body"] # Has the class "__panel_body" and
[./div[ # Contains a div element that
#class="__panel_header" and # Has the class "__panel_header" and
text() = "General Risks" # Has the text "General Risks"
]]
Using the Body Text
Given the HTML, you could also just locate the section directly based on its text.
div(:general_risks_section, class: '__panel_body', text: 'General Risks')
Note that this assumes that the HTML given was not simplified. If there are actually other text nodes, this probably would not be the best option.

how to get the data using XPATH from div with display:none?

I want to extract data from a div element with the attribute 'display:none'.
<div class='test' style='display:none;'>
<div id='test2'>data</div>
</div>
Here is what I tried:
//div[#class = "test"]//div[contains(#style, \'display:none\')';
Please help.
Try several changes:
1) Just put normal quotes around "display:none", like you did for your class attribute and close with ]
2) Then your div with class test and your style attribute is one and the same, so you need to call contains also for the same div:
'//div[#class = "test" and contains(#style, "display:none")]'
or the quotes the other way around, important is, that you are using differnt quotes around the expression than inside the expression
"//div[#class = 'test' and contains(#style, 'display:none')]"
if this still does not work, pls post an error message

Using XPath expression how can i get the first text node immediately following a node?

I want to get to the exact node having this text: 'Company'. Once I get to this node I want to get to the next text node immediately following this node because this contains the company name. How can I do this with Xpath?
Fragment of XML is:
<div id="jobsummary">
<div id="jobsummary_content">
<h2>Job Summary</h2>
<dl>
<dt>Company</dt>
<!-- the following element is the one I'm looking for -->
<dd><span class="wrappable">Pinpoint IT Services, LLC</span></dd>
<dt>Location</dt>
<dd><span class="wrappable">Newport News, VA</span></dd>
<dt>Industries</dt>
<dd><span class="wrappable">All</span></dd>
<dt>Job Type</dt>
<dd class="multipledd"><span class="wrappable">Full Time</span></dd><dd class="multipleddlast"><span class="wrappable"> Employee</span></dd>
</dl>
</div>
</div>
I got to the Company tag with following xpath: //*[text()= 'Company']
Now I want to get to the next text node. My XML is dynamic. So I can't hardcode the node type like <dd> for getting the company value. But this is for sure that the value be in the immediate next text node.
So how can I get to the text node immediately after the node with text as Company?
If you cannot hardcode any part of the following-sibling node your xpath should look like this:
//*[text()='Company']/following::*/*/text()
assuming that the desired text is always enclosed in another element like span.
To test for given dt text, modify your xpath to
//*[text()='Company' or text()='Company:' or text()='Company Name']/following::*/*/text()
use //*[text()='Company']/following-sibling::dd to get the next dd.
You can even insert conditions for that dd and also go further in it.
following-sibling::elementName just looks for the next sibling at the same parent level that meets your requirements.
With no conditions, like above, it will get the next dd after the 'Company'.
The text is in the span so you might try
//*[text()='Company']/following-sibling::dd/span
Another clarifying example would be, let's say that you want to get also the next industries text for the current selected 'Company'.
Having //*[text()='Company',
you can modify it like this: //*[text()='Company']/following-sibling::dt[text()='Industries']/dd/span
Of course, instead of hardcoding the values for text(), you can use variables.
You can Use XPathNavigator and go on to every node type one by one
I think XPathNavigator::MoveToNext is the method you are looking for.
There is the sample code as well at..
http://msdn.microsoft.com/en-us/library/9yxc3x24.aspx
Use this general XPath expression that selects the wanted text node even when it is wrapped in statically unknown markup elements:
(//*[text()='Company']/following-sibling::*[1]//text())[1]
When this XPath expression is evaluated against the provided XML document:
<div id="jobsummary">
<div id="jobsummary_content">
<h2>Job Summary</h2>
<dl>
<dt>Company</dt>
<!-- the following element is the one I'm looking for -->
<dd><span class="wrappable">Pinpoint IT Services, LLC</span></dd>
<dt>Location</dt>
<dd><span class="wrappable">Newport News, VA</span></dd>
<dt>Industries</dt>
<dd><span class="wrappable">All</span></dd>
<dt>Job Type</dt>
<dd class="multipledd"><span class="wrappable">Full Time</span></dd><dd class="multipleddlast"><span class="wrappable"> Employee</span></dd>
</dl>
</div>
</div>
exactly the wanted text node is selected:
Pinpoint IT Services, LLC
Even if we change the XML to this one:
<div id="jobsummary">
<div id="jobsummary_content">
<h2>Job Summary</h2>
<div>
<p>Company</p>
<!-- the following element is the one I'm looking for -->
<dd><span class="wrappable"><b><i><u>Pinpoint IT Services, LLC</u></i></b></span></dd>
<dt>Location</dt>
<dd><span class="wrappable">Newport News, VA</span></dd>
<dt>Industries</dt>
<dd><span class="wrappable">All</span></dd>
<dt>Job Type</dt>
<dd class="multipledd"><span class="wrappable">Full Time</span></dd><dd class="multipleddlast"><span class="wrappable"> Employee</span></dd>
</div>
</div>
</div>
the XPath expression above still selects the wanted text node:
Pinpoint IT Services, LLC

Resources