I need to make a quick change to a page built in Scala in a Play app. Not really sure how to do this...
I have a variable #name that contains "foo" and I want to do this:
<div id="#name" class="#name_class">
and have it resolve to
<div id="foo" class="foo_class">
however Play is trying to look for a variable named #name_class
You can avoid a temporary variable with the following:
<div id="#name" class="#{name}_class"> ...
You can first concatenate the string and the display:
#val class_name = name + "_class"
And then assign:
<div id="#name" class="#class_name">
EDIT
As explained in the comment by #Chirlo the above will not work as of Play 2.1
you can put concatenation in a reusable block and use as such:
#mkClassString(name:String,tag:String):String = { name + tag }
and use it:
<div id="#name" class="#mkClassString(name,"_class")">
or use #defining as indicated by comment.
You can enclose with parenthesis, I haven't really tested with curly braces.
<div id="#name" class="#(name)_class"> ...
Related
I am migrating some JSP files to Thymeleaf and I have found the following problem. In the original JSP code there is a file that is included in lots of other JSP files that has a line like the following:
<c:set var="myVar" value="data-x=50 data-y=37" />
In the files which include this one the variable myVar is used like this:
<div class="myClass" ${myVar}>
setting several attributes that are used everywhere all at once. The main point here is that you set up myVar in one file and its values are used in all other files. If you need to change the value of the attributes you only need to change one file.
Trying to replicate this in Thymeleaf, I tried using the th:attr attribute in the following way. First, I set a variable in the model like this:
model.addAttribute("myVar", "data-x=50, data-y=37");
and then in Thymeleaf I do:
<div class="myClass" th:attr="${myVar}" >
but it does not work, it throws
TemplateProcessingException: Could not parse as assignation sequence: "${myVar}"
I've also have thought of using Javascript to add the attributes dynamically using JQuery, but I would prefer to do it in other way, since I did not built the application, it has a lot of scripting and I fear doing it through Javascript can have side effects.
Is there any way I can write directly in the HTML output using Thymeleaf?
One way you can accomplish this with preprocessing. Something like this will work with your example:
<div class="myClass" th:attr="__${myVar}__" />
(This does rely on ${myVar} being a valid string for the th:attr property, so you'll have to be careful with it.)
<form action="/assessments/1290/questions" id="questionform" method="post">
<div class="happiness-reaction__item animated fadeIn selected">
I often get upset over minor issues.
<div class="inputs">
<input id="choice_14832" value="14832" name="Answers[0].Choices" data-scv-reaction-id="strongly-disagree1" spellcheck="true" type="radio">
<label class="u" for="choice_14832">
<i class="sc-strongly-disagree"></i>
</label>
<input id="choice_14833" value="14833" name="Answers[0].Choices" data-scv-reaction-id="slightly-disagree1" spellcheck="true" type="radio">
<label class="u" for="choice_14833">
<i class="sc-slightly-disagree"></i>
</label>
<input id="choice_14834" value="14834" name="Answers[0].Choices" data-scv-reaction-id="none1" spellcheck="true" type="radio">
<label class="u" for="choice_14834">
<i class="sc-none"></i>
</label>
<input id="choice_14835" value="14835" name="Answers[0].Choices" data-scv-reaction-id="slightly-agree1" spellcheck="true" type="radio">
<label class="u" for="choice_14835">
<i class="sc-slightly-agree"></i>
</label>
<input id="choice_14836" value="14836" name="Answers[0].Choices" data-scv-reaction-id="strongly-agree1" spellcheck="true" type="radio">
<label class="u" for="choice_14836">
<i class="sc-strongly-agree"></i>
</label>
</div>
</div>
.....
I want to click on that radio buttons. Since ID & value etc change dynamically , I should be able to make a simple click via using XPATH , But i cant. I get no error but buttons stays not-clicked.
Check this picture please
So far I was not able to click by doing :
WebElement button = driver.findElement(By.xpath(".//*[#id='questionform']/div[1]/div/label[1]/i"));
button.click();
Edited: I added this info , just in case if some1 wants more detailed info about the page I am talking about. There must be a way to click that simple button without workarounds.
"I know that would be too much effort to ask , but you can go "supercareer.com" , and take Personality under assessments , and see what I am actually talking about. Note: doesnt have to be real mail , easy to register 1 click , no verification you can access the questions that I am talking about. Thats the only way to provide 100% info about my questions."
What you want to do in cases like this is to find a "container" element that holds all the choices that you want to select from. In this case, we can use the question text to find the DIV that contains all 5 choices using XPath. From there, you can select the desired choice based on the classname of the I tag, e.g. <i class="sc-strongly-agree">. So provided the question text and the desired choice, we can make our selection.
In cases like this where you are likely use the same code over and over, you generally want to create a function to handle this. In addition, since you are selecting one of 5 choices repeatedly, I prefer to use an enum. You could use String but you would have to handle a potential wide variety of inputs and if someone ever misspells or somehow typos the string, the script fails. An enum makes it easy to select the desired choice because the IDE always shows you the choices making it a no-brainer. If you typo the enum, it won't compile... and it's always better to know earlier if a script is going to fail. An enum is also a container for those choices so that if the class names ever change, you change the class names in the enum and all scripts are fixed.
The function
public static void selectChoice(String question, Choices choice)
{
driver.findElement(By.xpath("//div[contains(#class,'happiness-reaction__item')][contains(.,'" + question + "')]//i[#class='" + choice.value() + "']")).click();
}
The enum
public enum Choices
{
STRONGLYDISAGREE("sc-strongly-disagree"),
SLIGHTLYDISAGREE("sc-slightly-disagree"),
NONE("sc-none"),
SLIGHTLYAGREE("sc-slightly-agree"),
STRONGLYAGREE("sc-strongly-agree");
private String value;
Choices(String value)
{
this.value = value;
}
public String value()
{
return value;
}
}
and here's how you call it
selectChoice("I often get upset over minor issues.", Choices.STRONGLYAGREE);
if you can't use ID and Value - use any other attribute. In your case 'data-scv-reaction-id' is a good option)
//*[#data-scv-reaction-id='strongly-disagree1']
So let's understand it this way:
Each question has a reaction section.
Each reaction section has a multiple reaction buttons/icons.
I've faced such a scenario and it needs to be handled in the following manner:
public void reactToQuestion(String question, String reaction) {
// Get the question element
// For eg. question = "I always complete all aspects of my work."
By locQuestion = By.xpath("//*[text()='" + question + "']");
WebElement element = driver.findElement(locQuestion);
// Get the parent 'div' container of the question
WebElement container = element.findElement("..");
// Get reaction RELATIVELY to the question
// reaction = 'strongly-disagree1', 'slightly-disagree1', etc.
By locReaction = By.xpath(".//*[#class='inputs']/input[#data-scv-reaction-id=" + reaction + "]");
WebElement reaction = container.findElement(locReaction);
reaction.click();
}
Please try and let me know if there are any issues.
Good luck.
As a general xpath, first input from question, it could be something like:
//form[#id='questionform']/div//input
If you need any input for certain question:
//form[#id='questionform']/div[contains(#class, 'happiness-reaction')]//input
If you need certain answer for a certain question then:
//form[#id='questionform']/div[contains(#class, 'happiness-reaction')]//input[#data-scv-reaction-id='strongly-disagree1']
If you need to add params for question and/or answer then:
//form[#id='questionform']/div[contains(#class, 'part_of_question_class')]//input[#data-scv-reaction-id='desired_answer_attr']
These params can be stored in an array/list or any form you like.
All these can be done in css also:
form#questionform>div input
form#questionform>div[class*=happiness-reaction] input
form#questionform>div[class*=happiness-reaction] input[data-scv-reaction-id='strongly-disagree1']
You get the idea.
Here's a couple of tricks I use to keep my xpaths concise and more robust to change.
You can reference deeper elements than the one you want to select by adding .. to the end of your xpath. For example I'd use the <i class="x" /> to distinguish what to click and then use .. to select the parent <label />
You can use // in the middle of an xpath as a wildcard to navigate through elements that aren't interesting.
Eg: By.xpath("//form[#id='questionform']//i[#class='sc-strongly-disagree']/..")
By using Thymeleaf as template engine, is it possible to add/remove dynamically a CSS class to/from a simple div with the th:if clause?
Normally, I could use the conditional clause as follows:
Lorem Ipsum
We will be creating a link to the lorem ipsum page, but only if condition clause is true.
I'm looking for something different: I'd like the block to always visible, but with changeable classes according to the situation.
There is also th:classappend.
If isAdmin is true, then this will result in:
Yes, it is possible to change a CSS class dynamically according to the situation, but not with th:if. This is done with the elvis operator.
Lorem Ipsum
For this purpose and if i dont have boolean variable i use the following:
<li th:class="${#strings.contains(content.language,'CZ')} ? active : ''">
Another very similar answer is to use "equals" instead of "contains".
<li th:class="${#strings.equals(pageTitle,'How It Works')} ? active : ''">
If you just want to append a class in case of an error you can use th:errorclass="my-error-class" mentionned in the doc.
<input type="text" th:field="*{datePlanted}" class="small" th:errorclass="fieldError" />
Applied to a form field tag (input, select, textarea…), it will read the name of the field to be examined from any existing name or th:field attributes in the same tag, and then append the specified CSS class to the tag if such field has any associated errors
Just to add my own opinion, in case it might be useful to someone.
This is what I used.
<div th:class="${request.read ? 'mdl-color-text--grey-800 w500' : ''}"> </div>
Yet another usage of th:class, same as #NewbLeech and #Charles have posted, but simplified to maximum if there is no "else" case:
<input th:class="${#fields.hasErrors('password')} ? formFieldHasError" />
Does not include class attribute if #fields.hasErrors('password') is false.
What #Nilsi mentioned is perfectly correct. However, adminclass and user class need to be wrapped in single quotes as this might fail due to Thymeleaf looking for adminClass or userclass variables which should be strings. That said,
it should be: -
<a href="" class="baseclass" th:classappend="${isAdmin} ? 'adminclass' :
'userclass'">
</a>
or just:
<a href="" th:class="${isAdmin} ? 'newclass' :
'baseclass'">
</a>
If you are looking to add or remove class accordingly if the url contains certain params or not .This is what you can do
<a th:href="#{/admin/home}" th:class="${#httpServletRequest.requestURI.contains('home')} ? 'nav-link active' : 'nav-link'" >
If the url contains 'home' then active class will be added and vice versa.
Just in case someone is using Bootstrap, I was able to add more than one class:
How do I include a JavaScript variable inside thymeleaf for checking a condition?
Tried:
<div th:if="${myVariable == 5}">
//some code
</div>
but it's not working.
What you are trying to do won't work, since Thymeleaf processes the template on the server side and therefore the variables it has access to are the ones defined in it's model.
If you had myVariable in the model on which Thymeleaf is operating, it would work.
If what you want is to set the value of a Javascript variable in a Thymeleaf template, you can do the following:
<script th:inline="javascript">
/*<![CDATA[*/
...
var myVariable= /*[[${myVariable}]]*/ 'value';
...
/*]]>*/
</script>
Inside the script you could have any logic you want including hide/show etc.
Check out this part of the documentation for mode details.
With thymeleaf 3, the CDATA block had to be removed to reference my variable inline or the parser ignored it completely. This syntax worked successfully for me post upgrade:
<script th:inline="javascript">
var myVariable = [[${#vars.myVar}]];
...
</script>
Note: if you also referenced your variables as [[${#ctx.variables.myVar}]], it will no longer work in thymeleaf 3 as ctx no longer has a variable named variables.
I think I understand your question and I don't think it's possible. Thymeleaf does server side rendering and thus it won't use javascript variables since those won't be created on the server.
What you can try is write an initializer function that will hide/show certain DOM elements. You can approach this multiple ways for instance give ids to them and change their class list to add "display:none" style.
<script>
// <![CDATA[
...Your script, here
// ]]>
</script>
hello i need to output a java variable inside a javascript call inside a tag inside a jsp !
for example:
<% String param = "hello";%>
<dmf:checkbox name="checkbox"
onclick = "selectAll(<%=param%>)"
/>
the javascript generated is:
selectAll(<%=param%>),this); but I actually want something like
selectAllCheckBoxes(Hello),this);
That's not escaping. That's just printing a scriptlet variable as if it is a JavaScript variable.
Besides, your examples are confusing, they doesn't match each other and the Javascript code is syntactically invalid. I can at least tell that JavaScript string variables are to be surrounded by quotes. If you want to end up with
selectAllCheckBoxes('Hello', this);
where Hello should be obtained as a value of the scriptlet local name variable (the param is a reserved variable name, you shouldn't use it yourself), then you need to do
selectAllCheckBoxes('<%= name %>', this);
In the same way, if you want to end up with
onclick="selectAll('Hello')"
you need to do
onclick="selectAll('<%= name %>')"
That said, I strongly recommend you to stop using the old fashioned scriptlets which are been discouraged since more than a decade. JSP programmers were been recommended to use taglibs and EL only to make the JSP code more clean and robust and better maintainable. You can use taglibs such as JSTL to control the flow in the JSP page and you can use EL to access the "back-end" data. Your example can be replaced by:
<c:set var="name" value="Hello" />
...
selectAllCheckBoxes('${name}', this);
Generate the whole attribute value with a scriptlet, like this:
<dmf:checkbox name="checkbox"
onclick = "<%= "selectAll(" + param + ")" %>" />
maybe you are trying to achieve this?
var myVar = '<%= (String)request.getParameter("tab") %>';
loadtabs(myVar);