I am trying to achieve internationalization and I have the following html markup.
<p th:text= "${your_amount(${it.vm.getAmount})}"></p>
which generates html as
<p>Your Amount is: $</p>
Your Amount is: $ is exactly what my internationalized string is.
Ideally the result should be.
<p>Your Amount is: $123.24</p>
The it.vm.getAmount sort of doesn't get executed. I have checked that there is a value present inside getAmount with the following
<p th:text="${it.vm.getAmount}"></p>
Which gives me a result as
<p>123.24</p>
Does thymeleaf consider "$" to be a special character while rendering and is there a way to work around that?
This expression:
<p th:text= "${your_amount(${it.vm.getAmount})}"></p>
is not valid syntax -- for many reasons.
${} expressions are never nested inside other ${} expressions -- unless you are preprocessing.
If your_amount is a string, you cannot use it as a function your_amount(...).
it.vm.getAmount shouldn't work -- unless you really have named your getter method getGetAmount(). It should either be it.vm.amount or it.vm.getAmount().
When I try your expression, I get this error:
Method your_amount(java.lang.String) cannot be found on org.thymeleaf.spring4.expression.SPELContextMapWrapper
If you just want to append different strings together, you should really be doing something like:
<p th:text= "${your_amount + it.vm.getAmount}"></p>
or
<p>
<span th:text="${your_amount}"><span th:text="${it.vm.getAmount}">
</p>
or
<span th:text="|${your_amount}${it.vm.getAmount}|" />
Related
I use Thymeleaf as a templating engine and I usually output variable value like this:
in Java I set:
ctx.setVariable("tester", "hello");
and in html template I output:
<span th:text="${tester}"></span>
This works great, but I would like to output a variable without the need of a tag. Something following would be great:
${tester}
Unfortunately it does not work. My goal is to avoid unnecessary tag to output the variable value. Is this possible to do with Thymeleaf?
My goal is to avoid unnecessary tag to output the variable value. Is this possible to do with Thymeleaf?
Yes this is possible. You can use the Thymeleaf synthetic th:block tag (see here).
Example template excerpt:
<body>
<th:block th:text="${tester}"></th:block>
</body>
This renders the following HTML:
<body>
hello
</body>
Only the variable is displayed.
Use Thymeleaf expression inlining (docs) using either [[...]] or [(...)]. With expression inlining, you do not need to use synthetic tags.
Example:
<body>
The value of tester is [[${tester}]].
</body>
Thymeleaf triggers on the "th:" tag and as far as I know thats the only way.
The behaviour you describe works with JSF.
Best regards
Ben
I also managed to figure out some workaround:
<span th:text="${tester}" th:remove="tag"></span>
th:remove removes span tag, but preserves content.
I am trying to select a channel from a series of channles that are displayed in a HTML table. I'm using the following Selenium method to select the link
WebElement channel = driver.findElement(By.xpath("//span[contains(text(),Sales)]"));
channel.click();
However it's selecting the first channel in the list (Account Management) instead. I would expect that it would either select the correct channel or throw an error, rather than select the wrong one. The following is the full xpath of the channel I want:
/html/body/div[2]/div[2]/form/div/table/tbody[2]/tr/td/ul/li[2]/a/span
The list of channels is defined like this in the HTML code:
<form action="nextpage.do" method="post" name="selectChannelForm">
<div class="de">
<h2>Select channel</h2>
<table id="selectChannelForm">
<tbody id=""></tbody>
<tbody id="">
<tr rowtype="container">
<td class="desecond" colspan="3">
<ul>
<li>
<a id="selected_a" href="nextpage.do?selectedChannel=123">
<span>Account Management</span></a>
</li>
<li>
<a id="selected_a" href="nextpage.do?selectedChannel=456">
<span>Sales</span></a>
</li>
<li>
<a id="selected_a" href="nextpage.do?selectedChannel=789">
<span>Complaints</span></a>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
<input type="hidden" value="selectChannelForm" name="formid">
</div>
First - your mistake!
You forgot the quotation marks around "Sales", just change your code a bit and it will work:
WebElement channel = driver.findElement(By.xpath("//span[contains(text(),'Sales')]"));
channel.click();
Second - xpath bug?
You're right that it is weird, that you are not getting an error message but instead the first element that is a span.
This might acutally be a bug in xpath. The contains functions realizes that your second argument is no a string, but instead of returning false, it returns true.
It actually hits all three of the span items. You only get the first as a result because you used the findElement function.
Try this and you will see the quirk:
System.out.println(driver.findElements(By.xpath("//span[contains(text(),Sales)]")).size());
Result will be:
3
Third - might be "as designed"
Having a look at the w3c definition you will find the following line:
If the value of $arg2 is the zero-length string, then the function
returns true.
Then on the xpath-site of microsoft you will find another interesting hint to the puzzle:
If an argument is not of type string, it is first converted to a
string and then evaluated.
Putting all this information together, I guess, xpath interprets your non-string/non-variable second parameter as an empty string and therefore returns true for all span elements since you were searching for //span.
UPDATE
From #MichaelKay in the comments we learn, that my "guess" was pretty close:
In XPath and XQuery, a bare name like "hello" means child::hello, and
if you're not using schema-awareness, then the system will just look
for children called hello, and if there aren't any, it will return an
empty node-set.
Conclusion: The behaviour the OP sees is as designed, even though it seems pretty non-intuitive.
The xpath that you are using is missing quotes "" around Sales text. text() function takes an argument that is a string and a string can be formed using quotes. Update your xpath in the following way -
driver.findElement(By.xpath("//span[contains(text(),'Sales')]")).click();
Or if you want to assign it to a WebElement, then do put in your quotes -
WebElement channel = driver.findElement(By.xpath("//span[contains(text(),'Sales')]"));
channel.click();
If at all you want to write nested double quotes or nested single quotes then use an escape character \ to write it. Here's how -
WebElement channel = driver.findElement(By.xpath("//span[contains(text(),\"Sales\")]"));
channel.click();
Hope this helps.
The xpath need to be modified as "//span[contains(text(),'Sales')]" .
As we can see below in the method definitions, contains method will return true only If second parameter is also a text.
Source: https://en.wikipedia.org/wiki/XPath
contains(s1, s2)
returns true if s1 contains s2.
text()
finds a node of type text
I am in a situation where the "class" attribute of a div tag should be dependent on the value of a java binding. This can be easily done by moving the associated logic to the java class, but at this moment we are not allowed to change anything at the Java component.
I am trying out the following to resolve the problem (using WOOGNL):
<div class="<wo:WOConditional condition = \"[cssClassDecider]\">classToUse</wo:WOConditiona>" >
HTML Static Content
</div>
As it can be seen, i am trying to use value of "cssClassDecider" to set the class.
Can anybody tell if any has solved a similar problem or one is available at WO.
It's not clear to me whether cssClassDecider is providing the string content for the class attribute, or a boolean to drive a conditional. In any case, the usual pattern would be:
<wo:WOGenericContainer elementName="div" class="$methodReturningClassNames">
...
</wo:WOGenericContainer>
If cssClassDecider returns a conditional, you could do something like this:
<wo:WOConditional condition="$cssClassDecider">
<div class="classWhenTrue">
...
</div>
</wo:WOConditional>
<wo:WOConditional condition="$cssClassDecider" negate="$true">
<div class="classWhenFalse">
...
</div>
</wo:WOConditional>
If neither of those solve your problem, provide some more information.
I have a web application running Java Tapestry, with a lot of user-inputted content. The only formatting that users may input is linebreaks.
I call a text string from a database, and output it into a template. The string contains line breaks as /r, which I replace with < br >. However, these are filtered on output, so the text looks like b<br>text text b<br> text. I think I can use outputRaw or writeRaw to fix this, but I can't find any info for how to add outputRaw or writeRaw to a Tapestry class or template.
The class is:
public String getText() {
KMedium textmedium = getTextmedium();
return (textmedium == null || textmedium.getTextcontent() == null) ? "" : textmedium.getTextcontent().replaceAll("\r", "<br>");
}
The tml is:
<p class="categorytext" id="${currentCategory.id}">
${getText()}
</p>
Where would I add the raw output handling to have my line breaks display properly?
To answer my own question, this is how to output the results of $getText() as raw html:
Change the tml from this:
<p class="categorytext" id="${currentCategory.id}">
${getText()}
</p>
To this:
<p class="categorytext" id="${currentCategory.id}">
<t:outputraw value="${getText()}"/>
</p>
Note that this is quite dangerous as you are likely opening your site to an XSS attack. You may need to use jsoup or similar to sanitize the input.
An alternative might be:
<p class="categorytext" id="${currentCategory.id}">
<t:loop source="textLines" value="singleLine">
${singleLine} <br/>
</t:loop>
</p>
This assumes a a getTextLines() method that returns a List or array of Strings; it could use the same logic as your getText() but split the result on CRs. This would do a better job when the text lines contain unsafe characters such as & or <. With a little more work, you could add the <br> only between lines (not after each line) ... and this feels like it might be a nice component as well.
I have a Stripes JSP with a <stripes:form> tag etc.
I have a line like the following one:
<span class='amount'>
<fmt:formatNumber value="${MyJavaClass.amount}" type="number"/>
</span>
I know this is working fine in combination with the corresponding Stripes Action Bean.
Now I want to format that "amount" variable in a different way, i.e. being sure that at least 2 decimals are used.
For example:
199.1 becomes 199.10
362.44 remains 362.44
I thought that adding a formatPattern="decimal" would have been enough, according to Stripes documentation.
Please note that I want to keep the "number" format type, I do not want to change it to be a currency type.
Actually I am experiencing errors like the following one:
org.apache.jasper.JasperException: /MyJSPPath/MyJSP.jsp(19,6) Attribute formatPattern invalid for tag formatNumber according to TLD
How can I fix it?
I fixed it setting properly the pattern parameter.
<fmt:formatNumber value="${MyJavaClass.amount}" type="number" pattern="#.00"/>
This was not enough:
<fmt:formatNumber value="${MyJavaClass.amount}" type="number" maxFractionDigits="2"/>
The issue related to only one digit was still there (i.e. "199.3" was still "199.3", rather than "199.30")