Why does the Spring form:hidden tag print two value attributes? - spring

Basic question, but haven't found any other relevant posts on SO.
I have this Spring code in my JSP with a property called vacant:
<form:hidden path="vacant" value="false"/>
And here is the generated output:
<input id="vacant" name="vacant" value="false" type="hidden" value=""/>
Why would value get printed twice with an empty second one?
(It's relevant because I'm trying to use the value in some Javascript.)

What you're seeing is "normal". According to the doc:
This tag renders an HTML 'input' tag with type 'hidden' using the
bound value
Let's assume vacant is Boolean vacant; in your DTO, since its value is null, the tag'll print it as value="". In addition, it'll print any other field you pass to it, e.g:
<form:hidden path="vacant" my-field="test"/>
<input id="vacant" name="vacant" my-field="test" type="hidden" value=""/>
So if you happen to use value in <form:hidden path="vacant" my-field="test" value="true"/>, it'll consider it an additional field:
<input id="vacant" name="vacant" my-field="test" value="false" type="hidden" value=""/>
Here is what happens in the source:
org.springframework.web.servlet.tags.form.HiddenInputTag
protected int writeTagContent(TagWriter tagWriter) throws JspException {
...
writeDefaultAttributes(tagWriter); // *) Here it'll print the value that you passed to the tag
...
//The next two statements get the bound value of vacant (null) and print it as value=""
String value = getDisplayString(getBoundValue(), getPropertyEditor());
tagWriter.writeAttribute("value", processFieldValue(getName(), value, "hidden"));
*) writeDefaultAttributes() calls writeOptionalAttributes(), where your passed value is printed (it's in this.dynamicAttributes along with my-field):
if (!CollectionUtils.isEmpty(this.dynamicAttributes)) {
for (String attr : this.dynamicAttributes.keySet()) {
tagWriter.writeOptionalAttributeValue(attr, getDisplayString(this.dynamicAttributes.get(attr)));
}
}
So, http:input is intended to be used for bound values, so set the value you need in the DTO before rendering the JSP.

Related

Pass through variable in Spring boot / Thymeleaf

I am looking for a way to pass the whole object through without having to use <input type="hidden" /> on the different variables. It seems like the th:object will not carry over the incoming information on the "whole object"
<form action="#" th:action="#{/api/result/save}" th:object="${result}" th:method="post">
<!--- Input fields -->
<input type="hidden" th:field="${result}"> <---- Not working.
<button type="submit" class="btn btn-primary" value="spara">Spara</button>
From the model I have
Result result = new Result(teams);
result.setTeam1ID(aTeam1.get().getId()); // This variable will not be changed in the HTML so I would like to pass that to the next page
// Other variables
When I get to the /save the Result will only contain variables set in Thymeleaf it will not retain the information from the original model above.
#PostMapping("/save")
public RedirectView saveResult(Result result, Model model) {
service.saveResult(result);
Thymeleaf is use to generate view, you can store some variable but not the whole object.
Though you can try these method:
If you object is already stored at server-side in memory or data base. You just pass the unique key of that object get it back using hidden input type.
Store the object into session then get it from there whenever requered.

Why do I lose information after submit a form with Spring MVC?

As I say int the title I loose information in the object that comes back from JSP to Controller.
From my Controller I pass a ModelAndView with an object of class Historic.
In the JSP page I have access to all of the values of this object, but when I submit I just get part of this information, some looses on the way on.
Controller:
#GetMapping("/tt")
public ModelAndView index(Model model) {
HistoricBO historic = new HistoricBO();
// ... I fulfill this object ...
return new ModelAndView("tt", "historic", historic);
}
In JSP I have access to all the information that I passed.
I use the values in two different ways. The first one (information that later I won't be able to recover) is:
<form:form method="POST" action="/addInput" modelAttribute="historic">
....
<form:label path="userHistoric[0].user.name" />
<form:input path="userHistoric[0].user.name" disabled="true" />
Being userHistoric a list inside HistoricBO object.
And the other way that I use the object values is daoing loop to the registers and show them. I can have these values after submit:
c:forEach items="${historic.userHistoric[0].periods[0].registers}" var="reg" varStatus="rog">
...
<td class="tab-odd">
<form:input path="userHistoric[0].periods[0].registers[${rog.index}].hours[0]" class="monin" type="number" />
</td>
The method that catch the submit is as follows:
#PostMapping("/addInput")
public String savePeriod(
#ModelAttribute("historic") HistoricBO inputs,
BindingResult result, ModelMap model) {
if (result.hasErrors()) {
return "error";
}
...
And here the object inputs only has setted the hours values, the rest of the object is empty.
Can you please why is the info loosing and how to solve it?
Thanks
Remove disabled="true" and use readonly="true" or readonly="readonly" instead like below.
<form:input path="userHistoric[0].user.name" readonly="readonly" />
Disabled values will not be submitted with the form.
See this values-of-disabled-inputs-will-not-be-submitted and demo here.

Thymeleaf + Spring: get rid of the default element id

Is there any way to suppress auto-generating ID attribute for elements while using th:field in Thymeleaf (2.1.4.RELEASE)? For example, given code:
<input type="text" th:field="*{year}" />
will produce the following HTML:
<input type="text" id="year" name="year" value="" />
What I want to achieve is (no id attribute):
<input type="text" name="year" value="" />
In JSP it was as easy as setting empty id:
<form:input path="year" id="" />
but Thymeleaf just replaces this empty attribute with the default-generated one.
Ok, I have looked inside the source code of Thymeleaf (2.1.4.RELEASE) and the method responsible for setting element id in Spring dialect is org.thymeleaf.spring4.processor.attr.SpringInputGeneralFieldAttrProcessor.doProcess(...) (source on Github) that calls org.thymeleaf.spring4.processor.attr.AbstractSpringFieldAttrProcessor.computeId(...) (source on Github). If you look at computeId(...), you will see that there is no simple way to set empty id.
So we need to do it in not a simple way :) Here it is:
I created a custom dialect and defined a custom attribute noid. The markup looks like this:
<input type="text" th:field="*{year}" thex:noid="true" />
There is a great tutorial explaining how to create and use custom dialects in Thymeleaf and below is the most important part: attribute processor responsible for removing id attribute from given element.
Important things to note:
high precedence value (9999) guarantees that this processor will be executed as the last one (so no other processors will modify id after this one is executed)
modification type is set to substitution so we are completely replacing value of id element
removeAttributeIfEmpty(...) returns true, rather self-explanatory, remove attribute if empty
getModifiedAttributeValues(...) sets id to empty value and because above-mentioned method returns true, id attribute is removed
Code:
public class NoIdAttrProcessor extends AbstractAttributeModifierAttrProcessor {
public NoIdAttrProcessor() {
super("noid");
}
#Override
public int getPrecedence() {
return 9999;
}
#Override
protected ModificationType getModificationType(Arguments arguments, Element element, String attributeName, String newAttributeName) {
return ModificationType.SUBSTITUTION;
}
#Override
protected boolean removeAttributeIfEmpty(Arguments arguments, Element element, String attributeName, String newAttributeName) {
return true;
}
#Override
protected boolean recomputeProcessorsAfterExecution(Arguments arguments, Element element, String attributeName) {
return false;
}
#Override
protected Map<String, String> getModifiedAttributeValues(Arguments arguments, Element element, String attributeName) {
Map<String, String> values = new HashMap<>(1);
values.put("id", "");
return values;
}
}
If you dont want to use this in id of you input field just assign the value to only the th:name field,
<input type="text" th:name="*{year}" />
will give you output like,
<input type="text" name="2015" />
Or You can use a string at the end to make the id generate different from the name attribute like this
<input type="text" th:name="*{year}" th:id="*{year} + '-year' " />
will give you the output,
<input type="text" name="2015" id="2015-year"/>

Internationalized Labels for Form Components in Wicket

How do I correctly create internationalized labels for my form components so that when displaying feedback messages an internationalized field name is displayed instead of the name of the field in the java code?
I've read this:
https://cwiki.apache.org/WICKET/everything-about-wicket-internationalization.html
as well as the documentation for wicket's xhtml tags:
https://cwiki.apache.org/WICKET/wickets-xhtml-tags.html
<label wicket:for="name">
<wicket:label>
<wicket:message key="label.name"/>
</wicket:label>
</label>
<input wicket:id="name" type="text" wicket:message="placeholder:label.name" />
This results in the following error:
Last cause: Expected close tag for '<wicket:label>' Possible attempt to embed
component(s) '<wicket:message key="label.name"/>' in the body of this
component which discards its body
If I replace the wicket:message with some arbitrary text it displays the text in any associated feedback messages.
(There's a related jira issue: https://issues.apache.org/jira/browse/WICKET-3903 however I still do not understand what has been done to fix this and what I must do ...)
Just found out there is a way to do this in java:
add(new TextField<String>("name").setRequired(true).setLabel(new Model<String>(getString("label.name"))));
Is it possible to somehow do this in a more comfortable way?
I just tested the following:
<form wicket:id="form">
<label for="input"><wicket:message key="input">some input</wicket:message></label>
<input wicket:id="input" type="text" name="input">
<input type="submit" value="submit">
</form>
And in the java class:
Form<HomePage> form = new Form<HomePage>("form"
, new CompoundPropertyModel<HomePage>(this));
wmc.add(form);
TextField textField = new TextField("input");
textField.setRequired(true);
form.add(textField);
In the property file I provided:
input=SomeInputField
This led to the following screen (if I leave the requiered field empty and press submit.
Is this what you are looking for?
Here is an alternative approach to #bert's that has always worked for me (wasn't aware of <wicket:label>)
The text shown for a FormComponent when a validation error occurs can be specified by means of FormComponent.setLabel(IModel). The shown text will be the result of the IModel's getObject().
TextField comp = new TextField("comp");
// Use internationalized text from XML resource file
comp.setLabel(new StringResourceModel("formResources.comp.label", this, null));
Notice this has nothing to do with <label> nor FormComponentLabel. FormComponentLabel is a component that can be used to model <label> tags.
You could even subclass FormComponentLabel to provide the label text based on FormComponent.getLabel(), and maybe output an extra mark when the field is required:
public class MyLabel extends SimpleFormComponentLabel{
private boolean required;
public MyLabel (String id, LabeledWebMarkupContainer labelProvider) {
super(id, labelProvider);
if (labelProvider instanceof FormComponent){
required = ((FormComponent)labelProvider).isRequired();
}
}
protected void onComponentTagBody(final MarkupStream markupStream,
final ComponentTag openTag) {
String mark = "";
if (required){
// could be for instance "*"
mark = getString("formResources.requiredField");
}
String text = getModelObjectAsString() + mark;
replaceComponentTagBody(markupStream, openTag, text);
}
}
{
TextField component = new TextField("component");
component.setRequired(true);
component.setOutputMarkupId(true);
IModel labelModel = new StringResourceModel("formResources.component.label",
this, null);
component.setLabel(labelModel);
add(component);
add(new MyLabel("componentLabel", component);
}
<label wicket:id="componentLabel"/>
<input type="text" wicket:id="component"/>
This way you would have clean way of
Setting the FormComponent's text to an internationalized resource string
Reusing exactly the same resource string transparently for the <label> tag and even adding custom marks to it based on FormComponent's properties.
Another alternative is to use the key attribute of <wicket:label/>, like so:
<label wicket:for="name">
<wicket:label key="label.name">Placeholder label</wicket:label>
</label>
<input wicket:id="name" type="text"/>
Unfortunately this attribute is not documented on the wiki page describing wicket's xhtml tags. All attributes supported are documented using JavaDoc in the class handling the tag (org.apache.wicket.markup.html.form.AutoLabelTextResolver).
The advantage of this alternative is that there is no additional coding required.
Wicket throws an exception to tell you that your <wicket:message> tag will be removed because the body of the <wicket:label> tag is replaced. The problem is you cannot nest the <wicket:message> tag inside the <wicket:label> tag (and shouldn't need to).
either this (Option 1):
<label wicket:for="name">
<wicket:label key="label.name"/>
</label>
<input wicket:id="name" type="text />
or this (Option 2):
<label wicket:for="name">
<wicket:message key="label.name"/>
</label>
<input wicket:id="name" type="text />
should work for you and result in HTML something like the following (assuming the properties file contains label.name=Name):
<label for="someMarkupId">
Name
</label>
<input id="someMarkupId" type="text" />
The difference is that if you set the label for the component through the Java code like so:
component.setLabel(new Model("value set in code"));
then using the Option 1 will result in the label being set to "value set in code", while using Option 2 will still result in the label set to "Name". Also if the label is set through Java code, and the key is missing from the properties file the Option 2 will throw an exception, while Option 1 will simply use the value set in the code.
I prefer this:
<label wicket:for="name"><wicket:label />:</label>
<input type="text" wicket:id="name"></input>
Just make sure to set the label in the FormComponent using setLabel, so the only java needed is:
add(new TextField("name", nameModel).setLabel(Model.of("i18n.name")));
This will be rendered as (in Dutch):
<label id="name63-w-lbl" for="name63">Naam:</label>
<input type="text" value="" name="name" id="name63">

Posting an array of Guid pairs to an Action

As you can see here, I'm allowing a user to dynamically create a table of data, and storing the ids of the table in a hidden field (in the example it's a text area so you can see it, and the final solution will be Guid rather than integers).
My question is simply this: What data type should I use on the server/MVC action to take the data held in the textarea/hidden field?
At the moment I have a string, and am contemplating doing a load of .split()'ing and whatnot, but it doesn't feel right!
Ultimately I need some sort of IEnumerable<Guid, Guid> thing?!?! so I can do a foreach and get each pair of Ids.
I'm sure the answer will be simple, but I can't think of what to do.
Any help appreciated.
If your UI has multiple, like-named form fields, they will be submitted to your action method and bound properly to an array. We could use string[] for this case.
<form action="">
<input type="text" name="guids"/>
<input type="text" name="guids"/>
<input type="text" name="guids"/>
<input type="text" name="guids"/>
<input type="submit" value="Submit"/>
</form>
Then your controller could handle them like so:
public ActionResult MyAction(string[] guids)
{
guids.Count == 4 // if all four fields were filled in.
}
Note that if there is just a single guids value sent by the form, the string[] guids will still work - it will contain just a single item.
Finally, note that if no values are entered, the array value will be null, not an empty array.
You can actually bind to a list from your model, take a look at this post
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

Resources