StrutsCatalog: Here are TWO WAYS to take care of that pesky and recurrent problem of how to use multiple image buttons in your forms. This solution will suggest other possibilities you might want to code for yourself. Use the one you like best.

SOLUTION ONE: THE PREFERRED SOLUTION

First, you can merely mine the parameterNames of the request and build your logic in your processing of the request accordingly. This is by far the best solution, in my opinion. It is simple and extensible.

public final class ButtonCommand {
  private ButtonCommand() {
  }

  public final static String getCommand(HttpServletRequest request) {
    Enumeration enum = request.getParameterNames();
    String parameterName = null;
    while(enum.hasMoreElements()) {
      parameterName = (String)enum.nextElement();
      if(parameterName.endsWith(".x")) {
        return parameterName.substring(0,parameterName.indexOf('.'));
      }
    }
    return parameterName;
  }
}

I use this with the following simple logic in a processing class called by my Action class. You can do whatever you like to use the results of the ButtonCommand class. You don't, of course, have to have a Struts solution to use this.

Assume that you have code like:

<input type='image' name='submit'>

Or:

<html:image property='submit'/>

or whatever else you may have going on. Actually I automatically generate and cache buttons of the right size, color, background, etc. on the fly in 15 languages as follows:

<crackwillow:image
  button='HOSTS.gif'
  mapBean='host_container'
  bgClr='ffffff'
  txtClrCode='banMddlBgClr'
  font='Eurostile'
  italic='false'
  bold='true'
  size='20'
  property='hostsOptions'/>

But, none of this is related to the solution. The solution is essentially unccnnected to other issues. The point is that the preceding solution is not tied to other decisions but merely to utilizing what we need to get the job done with

<input type='image>

We use the class as follows.

if ("submit".equals(ButtonCommand.getCommand(request))) {
  // do whatever
}

Obviously the use is significant when we have more than one use of the image tags. That is the whole point.

This solution is very, very simple and very, very effective. I think it is the best out there. You can stop here. But, if you are interested in sort of "legacy thinking", then the following is "interesting" and, I think, an improvement on the normal way that button classes are handled with image tags.

Most importantly, there is very, very little overhead either in footprint or in calculations with the previous solution. A VASTLY inferior, I think, but more typical solution follows.

SOLUTION TWO: THE TYPICAL BUT INFERIOR SOLUTION TYPE.

Second, if you want something ostensibly more OOP in nature, then the following might be your choice. I have come to think the following is a mistake and but another instance of overthinking a solution. This second option is a bit better than some solutions, since it has a small footprint comparatively. But it is really a dinosaur in my opinion.

The Struts page tags are simple. If you have buttons you want called "submit" and "clear" for example, you would have the following page tags:

  <html:image property='button.submit' src='Submit.gif'>
  <html:image property='button.clear' src='Clear.gif'>

The resultant HTML producted by the magic html-image tag is:

  <input type=image name='button.submit' src='Submit.gif'>
  <input type=image name='button.clear' src='Clear.gif'>

PLEASE NOTE THAT THE name attribute in the HTML or the property attribute in the Struts html-image tag is the name of the command or operation, not the "name" of the button. The "name" of the button, or "reference" of the button, is the src attribute. Therefore, you could use the following as well:

  <html:image property='button.submit' src='CLICK.gif'>
  <html:image property='button.clear' src='GO_BACK.gif'>

I used to just use a button and encorporated various Commands like Submit Clear, etc. Discussions with Larry Young of www.dalmatian.com convinced me that my solution was way to heavy-handed. Now, like the bloke who was turned into a newt but "got betta", my new and improved solution is to provide a ButtonForm to subclass or to copy, whatever excites you. Here is the ButtonForm (if you want more buttons, just add to the getters, e.g. getSubmit():

public class ButtonForm
    extends ActionForm {
  protected CrackWillowButton button;
  protected String command;

  public CrackWillowButton getButton() {
    if(button == null) { this.button = new CrackWillowButton(); }
    return button;
  }

  public String getCommand() {
    String hold = command; command = null;
    return hold;
  }

  public void reset() {
    button = null;
  }

  public class CrackWillowButton {
    private Integer x, y;
    public CrackWillowButton() { }
    public CrackWillowButton getSubmit() { command = "submit";    return button; }
    public CrackWillowButton getClear()  { command = "clear";    return button; }

    public void setX(Integer x) {
      if(y != null) { reset(); }
      else          { this.x = x; }
    }

    public void setY(Integer y) {
      if(x != null) { reset(); }
      else          { this.y = y; }
    }
  }
} ///;-) 

Somewhere in the model, wherever we want to employ our logic, we check out which button type has been pressed as follows:

  String command = buttonForm.getCommand();
  if("submit".equals(command)) {
    // do whatever
  } else if ("clear".equals(command)) {
    // do whatever
  } else {
    // some assert code
  }

Enjoy! And, thanks, Larry Young, for spurring me on to better ideas! Namaste!

– Michael McGrady

michael@michaelmcgrady.com

N.B. The extension below conflates two differing ways of choosing what processing is required. If the image button is used to make this choice, then there is no need for a dispatch action. If you don't want to use the image to make this choice, that is fine. But, using the two together is a mistake, in my opinion. The solution given below also uses the second solution given here, which is by far the least prefered. So, my suggestion is that you take the "extension" below with more than a grain of salt. The extension just complicates a simple solution.

The point is this: if you use the LookupDispatchAction solution, you don't need this solution. If you use these solutions (either one), you don't need the LookupDispatchAction solution. Two solution in one is not better in this case. The ".x" and ".y" in the parameter names from an image tag operate very similarly to the parameter name for a DispatchAction. The only difference is that they are built into the way the HTML works, rather than in the way that the Struts specific DispatchAction and LookupDispatchAction classes work.

Lastly, please note that the LookupDispatchAction does not take the solution away from the execute method of the Action class. Rather, it merely hides it in a super class. Personally I prefer to just call a process class in my Action classes within the execute method. So, for any XAction class, I have a XProcess class. The point of this is merely to decouple the Action and the business logic at the "get go". I am not fond of the dispatch methodology because it creates coupling rather than decoupling. I am also not fond of the dispatch methodology because it unnecessarily requires the configuration in the XML and unnecessarily uses the parameter attribute, which can be used for something else if left alone.


Just an extension.

Instead of checking the command value in the execute method of the Action, you can make your Action extend DispatchAction by overriding the protected dispatchMethod

public class TestAction extends DispatchAction {
       /**
        * Dispatch to the specified method.
        * @since Struts 1.1
        */
       protected ActionForward dispatchMethod(
               ActionMapping mapping,
               ActionForm form,
               HttpServletRequest request,
               HttpServletResponse response,
               String name)
               throws Exception {
               return super.dispatchMethod(mapping, form, request, response, 
                                          ((ButtonForm) form).getCommand());
       }

       public ActionForward submit(
               ActionMapping mapping,
               ActionForm form,
               HttpServletRequest request,
               HttpServletResponse response) {
               return mapping.getInputForward();
       } 

       // Add more methods corresponding to the number of images (submits)
}

OR by overriding the execute method

public class TestAction extends DispatchAction {
       public ActionForward execute(
               ActionMapping mapping,
               ActionForm form,
               HttpServletRequest request,
               HttpServletResponse response)
               throws Exception {

               // Identify the request parameter containing the method name
               String parameter = mapping.getParameter();
               if (parameter == null) {
                       String message =
                               messages.getMessage("dispatch.handler", mapping.getPath());
                       log.error(message);
                       response.sendError(
                               HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                               message);
                       return (null);
               }

               // Identify the method name to be dispatched to.
               // dispatchMethod() will call unspecified() if name is null
               String name = ((ButtonForm) form).getCommand();

               // Invoke the named method, and return the result
               return dispatchMethod(mapping, form, request, response, name);
       }

       public ActionForward submit(
               ActionMapping mapping,
               ActionForm form,
               HttpServletRequest request,
               HttpServletResponse response) {
               return mapping.getInputForward();
       }
       
       // Add more methods corresponding to the number of images (submits)
}

If we are dealing with too many images, then it's better to make it an DispathAction similar to the one illustrated above. One thing to remember is to define a parameter in the ActionMapping.

Thanks, Kishore Senji.


Thanks, Kishore,

If you use the first solution, there is no extra coding no matter how many images you use for buttons. You have to match what the property value is for <html:image> or the name value for <input type='image'> and make sure that matches your logic in the model, but that is all. That, of course, has to be done with any possible solution. This frees the parameter attribute of your action mapping for other uses. I, for one, have other very important uses for this parameter attribute. (I use it to allow a single mapping to go to multiple layouts in tiles.)

You also gain nothing from using the LookupDispatchAction in this case.

  • No labels