<!-- --><style type="text/css">@import url(https://www.blogger.com/static/v1/v-css/navbar/3334278262-classic.css); div.b-mobile {display:none;} </style> </head><body><script type="text/javascript"> function setAttributeOnload(object, attribute, val) { if(window.addEventListener) { window.addEventListener('load', function(){ object[attribute] = val; }, false); } else { window.attachEvent('onload', function(){ object[attribute] = val; }); } } </script> <div id="navbar-iframe-container"></div> <script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script> <script type="text/javascript"> gapi.load("gapi.iframes:gapi.iframes.style.bubble", function() { if (gapi.iframes && gapi.iframes.getContext) { gapi.iframes.getContext().openChild({ url: 'https://www.blogger.com/navbar.g?targetBlogID\x3d7256432\x26blogName\x3dThe+Frustrated+Programmer\x26publishMode\x3dPUBLISH_MODE_BLOGSPOT\x26navbarType\x3dBLACK\x26layoutType\x3dCLASSIC\x26searchRoot\x3dhttp://frustratedprogrammer.blogspot.com/search\x26blogLocale\x3den_US\x26v\x3d2\x26homepageUrl\x3dhttp://frustratedprogrammer.blogspot.com/\x26vt\x3d5012862196962223429', where: document.getElementById("navbar-iframe-container"), id: "navbar-iframe" }); } }); </script>
| Thursday, July 08, 2004

If you have a form with multiple buttons on it, when your action is invoked you need to find out which button was pressed. Usually this is done by each button passing the same parameter with a different value.

Something like this:

<html:form action="/foo.do">
[...]
<html:submit name="method" value="Add">
<html:submit name="method" value="Delete">
</html:form>


You could use a series of if/else statements to perform the correct logic based on the 'method' parameter, or you could extend DispatchAction and set the parameter attribtue to 'method'. Using a DispatchAction will use reflection to call a method with the name as the value of the 'method' parameter.

Using this solution still has a drawbacks:
-The value attribue of the html:submit tag is the method, exactly. If you need to change the name of the button for any visual reason (spelling, usablility, etc.) the method must change too.
-If your application supports multiple languages, this can't be used since it has to be an exactly one-to-one match.

The official party-line is to use a LookupDispatchAction, for more detailed information read Ted Husted's tip. Basically a Map is populated which links the name of the button to the method that should be called when the button is pressed. While this works fine, it's a little overkill to me.

Its a bit of a hack, but I prefer the following code. It encodes the name of the method to be called in the name of the submit button. The value of the submit button is only used for display purposes, nothing else.


<html:form action="/foo.do">
[...]
<html:submit name="method_add" value="Add">
<html:submit name="method_delete" value="Delete">
</html:form>


To make this work, your code that normally extends DispatchAction now needs to extend SimpleLookupDispatchAction, defined below:

public class SimpleLookupDispatchAction extends DispatchAction {
protected getMethodName(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String methodName) {
// First try to get the method the traditional way.
Method method = getMethod( methodName );

// If its not found, now try to find it based on the parameter name.
if( method == null ) {
methodName = getMethodName( request, mapping );
if( methodName != null ) {
return super.dispatchMethod(mapping, form, request, response, methodName);
}
throw new Exception("Unable to find parameter in post data: " + prefix);
} else {
return super.dispatchMethod(mapping, form, request, response, name);
}
}

/**
* Get the method name by using the SimpleLookupDispatchAction technique.
*/
public Method getMethod( HttpServletRequest request, Mapping mapping ) {
// loop through each parameter and try to find one starting with
String prefix = mapping.getParameter();
for( Enumeration e; request.getParameterNames(); e.hasMoreElements(); ) {
String paramName = (String) e.nextElement();
// The paramName should look like this: 'parameter_value.x', we need just 'value'
if (paramName.startsWith(prefix + "_")) {
String paramSplit = paramName.split("_");
String value = paramSplit[1];
// This last split is used to trim off the .x or .y if this was an image button.
methodName = value.split( "\\.")[0];
return methodName;
}
return null;
}
}


This is a lot less work, and I haven't had any problems with it yet...