Struts 2.3 form with multiple submit tags with action attribute - forms

This is something quite simple which worked perfectly with Struts 2.1.x. But we recently upgraded to 2.3.15.2 and it broke. Basically we have a form (actually, many forms) with multiple submits:
<s:form>
<s:submit action="save" />
<s:submit action="resetPassword" />
</s:form>
If I stick the action in the tag all is well. But if instead it is in the tag, I get a 404 error. And it's the same action!
I've been debugging and discovered that when you use the "action" attribute in the tag, the generated html is:
<input type="submit" name="action:save">
<input type="submit" name="action:resetPassword">
Supposedly Struts should take this "action" prefix and say "A-ha! This is an action!" and execute it. And it more or less does this. Or at least tries to. What I've discovered is that at a very low level, the DefaultActionMapper.handleSpecialParameters() method goes through all the parameters and tries to create a ParameterAction for each one, and if it's not null, it is executed. Most of the parameters produce a "null" ParameterAction, but not "action:".
In the docs I found this about ParameterAction:
Defines a parameter action prefix. This is executed when the configured prefix key is
matched in a parameter name, allowing the implementation to manipulate the action mapping
accordingly. For example, if the "action:foo" parameter name was found, and a
ParameterAction implementation was registered to handle the "action" prefix, the execute
method would be called, allowing the implementation to set the "method" value on the
ActionMapping
So what it does is set the mapping's result to a new ServletDispatcherResult with the name of the Action:
mapping.setResult(new ServletDispatcherResult(actionName));
On the other hand, when the action is specified in the s:form tag, the mapping's result is null.
So that when finally we get to Dispatcher.serviceAction():
if (mapping.getResult() != null) {
Result result = mapping.getResult();
result.execute(proxy.getInvocation());
} else {
proxy.execute();
}
So when the action is specified in the tag, proxy.execute() is called, which just calls the Action/method itself. Which is what should happen! But when the action is specified in the tag, since the mapping has a result, the proxy's invocation is passed to result.execute(), which calls ServletDispatcherResult ... and in the end, I get a 404.
This seems like a whole lot of work just to get multiple submit buttons with action attributes working. Is this a known issue for Struts 2.3? Do I need to implement a ParameterAction for the "action" prefix as stated in the docs?
EDIT
Ok, known bug, opened just a few days ago. In the meantime I can either downgrade to 2.3.15.1 or use the "method" attribute rather than the "action" attribute.
Hopefully it will be fixed soon ...

this is b/c in struts2.3.16.
Disables support for action: prefix
by default struts.mapper.action.prefix.enabled = false
set
<constant name="struts.mapper.action.prefix.enabled" value="true"/>
in struts.xml
Internal Changes of struts2-core 2.3.16
The action: and method: prefixes are be by default excluded and changed order to first check excludeParams and then acceptedParams in ParametersInterceptor

This is supposed to be in the process of being fixed for 2.3.15.3.
The specific jira is:
https://issues.apache.org/jira/browse/WW-4204

Related

Auto populate form input based on name?

If I have a Command Object such as
class AppContactInfoCommand {
String fname = "Tom";
}
When I pass that command object to my view to populate my form I have to do this
<g:textField name="cmd.fname" value="${cmd.fname}"/>
This just seems extremely repetitive and time consuming if I have large forms with many fields. Isn't there any way to to have the g:textField intelligently detect that value and auto-populate the value field so all I have to do is
<g:textField name="cmd.fname" />
Other frameworks I have worked with do this so Im sure there must be a way from Grails to do this too.
Grails Version: 3.1
There is nothing stopping you from writing your own tag library to do just that. However the included tag libraries for such things as the textField do not have this behavior. As can be seen from the documentation.

HTTP Status 404 rest url mapping error pagination next

I have a list.gsp that displays list of items which is restful. It displays well but It gives me error when I click next or page no. My mapping is:
"/request/list/$sort?/$order?/$max?/$offset?"(controller:"request"){
action = [GET:"list"]
}
My view pagination is:
<div class="paginationlayer">
<span >
<g:paginate next="Next" prev="Back"
total="${ total }" /></span>
</div>
I tried using name url mapping like this:
name requestURL: "/list/$sort?/$order?/$max?/$offset?"{
controller = 'request'
action = 'list'
}
and out some mapping in the view like this mapping="requestURL", I even added params in the pagination, or hardcoded params like offset, max etc but still the same.
but still it gives me HTTP Status 404 when I click "next" it seems that the url loses its map and becomes something like this : http://localhost:8081/client/request/%5BGET%3Alist%5D?offset=10&max=10&order=desc
as basic as it may sound, the solution was to put action="list" to the pagination. Didn't occur to me coz all my paginations work with out it.
<span class="gadgetNumber">
<g:paginate next="Next" prev="Back"
maxsteps="0" action="list"
total="${ printRequestInstanceTotal }" /></span>
There is no need to list all possible parameters in UrlMappings.groovy: whatever you may need will still be available via the params object or via action method attributes.
Please try rewriting the url mapping as
"/request/list"(controller:"request"){
action = [GET:"list"]
}
This will probably resolve your issue and save us the (possibly considerable) effort of determining why exactly your URL mapping isn't being accepted.

Set class or ID on <h:inputHidden> in JSF

I'm trying to set a class or id parameter on a <h:inputHidden> in JSF. The code looks like this:
<h:inputHidden value="#{getData.name}" class="targ" />
But in the browser, the class isn't set:
<input type="hidden" name="j_idt6" value="j_idt6">
I need to set a class to this parameter, because I have a JavaScript autocomplete function for a <h:inputText> that sets a value in the hidden input, which needs to be passed in the next page.
Any ideas? Thanks!
I know it's a little bit late, but it can help someone in the future.
As inputHidden shows nothing in the browser there's no sense to allow it to have a class.
You can use the Id but as the Id could change as you change the component parents using it would bring some headache.
I'd suggest as a workaround, you can give it a parent so you can manipulate it by javascript.
Exemple:
JSF
<h:panelGroup styleClass="someCssClass">
<h:inputHidden id="someId" value="someValue" />
</h:panelGroup>
Javascript (using jQuery, you could use pure javascript also)
$('.someCssClass input[type=hidden]').val('yourNewValue');
None of these answers here satisfied my needs (need the PrimeFaces component, need class not ID, wrapping is too much work), so here's what I came up with:
Use pass-through attributes: https://www.primefaces.org/jsf-2-2-pass-through-attributes
Use pass:hidden-class="blah" (in my case, it's xmlns:pass up top)
Use [attribute=value] selector:
https://www.w3schools.com/cssref/sel_attribute_value.asp
document.querySelector multiple data-attributes in one element
That basically boils down to using something like this (because h:inputHidden becomes a regular input): document.querySelector("input[hidden-class=" + blah + "]")
Please, see similar question - How can I know the id of a JSF component so I can use in Javascript
You can sed "id" property, but in final html code it can be not the same, but composite: for example, if your input with id="myhidden" is inside form with id="myform", final input will have id="myform:myhidden".
In the end, I used a standard HTML <input type="hidden"> tag, as I had no advantages for using the JSF one. If you're trying to set a value in a hidden input with JavaScript, I recommend using this workaround.

Grails giving me 404 on controller when submitting form

I have a basic controller, just like this:
class BookingController {
def periodCheck(){
}
def periodInput(){
}
Both booking/periodCheck.gsp and booking/periodInput.gsp have been created, and they work if I access typing the URL directly.
However, I have a form in periodInput.gsp that is supposed to send some data to periodCheck, and every time, when I send the form, I get a 404 error, saying that The requested resource (/hoteledison/booking/periodCheck) is not available. The form is defined like this:
<g:form action="periodCheck">
<!-- here would go the fields -->
<g:actionSubmit class="btn" value="Comprobar" />
</g:form>
What am I doing wrong? I've also tried defining allowedMethods in the controller, but that does not help.
Ok, according to this blog post, there's a difference between using actionSubmit and submitButton. For forms like mine, with just one action, I should have used the later.
Now it's working.

jQuery: Select all 'select' elements with certain val()

Does anyone know of an easy way, using jQuery, to select all <select> elements whose val() attribute yields a certain value?
I'm trying to do some validation logic and would like to just select all those elements with a single selector, then apply a warning class to each of their parents. This I know how to do once I select all the elements, but I didn't see a selector that handles this case.
Am I going to have to select all of the <select> elements into a selector, then iterate through them and check each of their values? I was hoping there would be a simpler way.
Thanks.
Why doesn't select[value=x] work? Well firstly because <select> doesn't actually have a value attribute. There is not a single value of a select box: there may be no selected options (there shouldn't normally be, but there can be in at least IE), and, in a <select multiple>, there can be any number of selected options.
Even input[value=x] doesn't work, even though <input> does have a value attribute. Well, it does work, it just doesn't do what you think. It fetches the value of the value="..." attribute in the HTML, not the current value you have entered into the form. The value="..." attribute actually corresponds to the defaultValue property and not value.
Similarly, option[value=x][selected] doesn't work because it is checking the <option selected> attribute from the HTML source (selected attribute -> defaultSelected property) and not the current selectedness of the option (selected property not attribute) - which might have changed since the page was loaded.
Except in IE, which gets the value, selected etc form attributes wrong.
Except (again): Tesserex's example may seem to work, and the reason for that is that that it's using a non-standard jQuery-specific selector, :has. This causes the native querySelectorAll methods of modern browsers to fail, and consequently jQuery falls back to its own (native JavaScript, slow) selector engine instead. This selector engine has a bug where it confuses properties for attributes, allowing [value=x] to do what you expected, and not fail like it should! (Update: this is probably no longer the case in newer jQuery versions.)
Summary: form field state checking and selectors don't mix. Apart from these issues, you also have to worry about escaping issues - for example, what if the value you want to test against contains quotes or square brackets?
So instead, yes, you should check it manually. For example using a filter:
$('select').filter(function() {
return $(this).val()==='the target value';
}).parent().addClass('warning');
(There is a value property in HTML5 and supported by modern browsers, that when you read it gives you the value of the first selected <option>. jQuery's val() is safe to use here because it provides the same method of getting the first selected option even on browsers that don't support this.)
The existing answers don't work on select tags, but I found something that does. Ask for a select that has a selected option.
$("select:has(option[value=blah]:selected)")
You can use :
$("select[value=X]");
where X is the value against which you want to check the select's value.
Attribute selectors Is what you're looking for I believe.
Something like $+('element[attribute="value"]')
See also:
*= anywhere
^= starts with
$= ends with
~= contains word
etc.
You can create a change event that puts the value in a custom attribute on the select element whenever the value changes. You can then use a simple selector to find all of the select elements that have that value. For example:
$("select").on("change", function (e) {
var $select = $(e.currentTarget);
$select.attr("select-value", $select.val());
});
And then you can do this:
var $matches = $("select[select-value='" + searchVal + "']");
$matches will have all of your matching selects.
This is a lot easier than having to iterate through elements. Remember to set select-value to the initial value when rendering the page so you don't need to trigger a change event for each select so the select-value is set.