See Ted's message below

-----Original Message-----
From:   Ted Husted [mailto:[EMAIL PROTECTED]] 
Sent:   17 June 2002 12:19
To:     [EMAIL PROTECTED]
Subject:        [MVC-Programmers] [Struts Tips] #12 - Use smart forwarding
to create menuing systems

One thing that has made the World Wide Web so popular is ease of navigation.
Any swatch of text on a page can be turned into a hyperlink.  The user just
needs to point-and-click, and off they go to the target page. Behind the
swatch of text is a path to the page, which may be long and cumbersome, but
the user doesn't need to know that. They just click on the description, and
the system does the rest. 
Besides the ubiquitous hyperlink, HTML also provides us with an assortment
of user interface widgets, like radio buttons, check boxes, submi buttons,
and select lists. Like hyperlinks, they allow us to display a plain language
description to the user, but return a technical descriptor to the server.
Most often, these controls are used to make it easier to fill-out a form,
but they can also be used to create menu systems. The user selects a
location from the select list, and the system whisks them off to the
relevant page. 
The simplest way to build a menu system would be to just embed the page
locations, or URLs, into the control. Any many, many web applications have
been written using that approach, especially those using CGI systems like
Perl. But, embedding systems paths is not the way most developers want to
build application with Struts. Most of us want to design pages using logical
identifiers, and let Struts do the matching between identifiers with system
paths. This way we can move things around without the pages being any the
wiser. 
The fundamental way Struts matches identifiers with system paths is through
the Struts configuration (struts-config.xml). Struts applications
continually use the configuration to match up ids like "success" and
"failure" to various locations within the application. Now how do the same
thing to support menuing systems?
In Tip #11, we looked at how the standard RelayAction can be used to select
between multiple submit buttons. Now lets look at how we can use the
RelayAction to select between multiple options on a select list. 
The simplest instance would be selecting between various locations in an
application. Here's an example with two options:
<html:select property="dispatch" >
<html:option value="reload">Reload Config</html:option>
<html:option value="create">Create Resources</html:option>
</html:select>
This controls can be used just like the multiple submit buttons in Tip #11.
The form's dispatch property is set to whatever is selected. The form is
submitted to a RelayAction with a local forward for each option.  The
RelayAction then forwards the request along to whatever path is indicated by
the forward. 
<action 
path="/menu/Manager"
type="org.apache.scaffold.struts.RelayAction"
name="menuForm"
validate="false">
<forward 
name="reload"  
path="/do/admin/Reload"/> 
<forward 
name="createResources"  
path="/do/admin/CreateResources"/> 
</action>
This is great for simple requests, but what if we need to include a
parameter with the action?
If the control is being used to select parameters for the same action, or
actions that use the same parameter name, you can just give the option the
parameter name. Here's a radio button control that displays identifiers like
"Day" and "Week" but passes the corresponding number of hours to the action.

<html:form action="/find/Hours">
<P>List articles posted in the last:</P>
<P>
<INPUT type="radio" name="hours" value="24">Day <INPUT type="radio"
name="hours" value="168">Week <INPUT type="radio" name="hours" 
value="720">Month
</P>
<P>
<html:submit property="submit" value="GO"/>
</p>
</html:form>

When they submit the form, the browser will generate a URI like 
/find/Hours?hours$ 
or 
/find/Hours?hours8 
or 
/find/Hours?hoursr0 
depending on which radio button is selected.
In practice, we might want to write this control from a collection,
using code like 
<html:form action="/find/Hours">
<P>List articles posted in the last:</P>
<P>
<html:options collection="FIND" property="value"
labelProperty="label"/>
</P>
<P>
<html:submit property="submit" value="GO"/>
</p>
</html:form>

But, that would not be an instructive example. So, we show our options
hardcoded instead, even if that is not what we would do in practice. 
Hardcoding a parameter, or passing it down with a collection, works fine
when all our options go to the same option, or go to actions that use the
same parameter name. But what if the parameters names are different?  We may
have a number of actions for looking up a record based on this field or that
field, and may need to provide the field as the parameter name, like this:
/do/find/Title?title=Struts
/do/find/Author?creator=husted
/do/find/Content?content=menus
/do/article/View?article
There are many times when we would like to provide locations like these as a
single combo control, that lets us select the search type (Title, Author,
Content, ID), and then provide a user-supplied parameter, like those shown. 
In each case, all we really need to do is paste the parameter to the end
of the URI. The form would still need to submit the parameter under the
same name, but if we could take something like 
/do/menu/Find?dispatch=title&value=Struts
and turn it into 
/do/find/Title?title=Struts
we'd be in business.
Since this is a common need, it's worth creating a standard action to do
just this. Here's the source for a simple parameter action that pastes a
value at the end of a partial URI. 
// Get "dispatch" parameter
String parameter = request.getParameter(Tokens.DISPATCH);
// Get parameter name for this mapping String paramName =
mapping.getParameter();
StringBuffer path = new StringBuffer(64);
// Get stub URI from mapping (/do/whatever?paramName=)
path.append(mapping.findForward(parameter).getPath());
// Append the value passed (/do/whatever?paramName=paramProperty)
path.append(request.getParameter(paramName));
// Return a new forward based on stub+value return new
ActionForward(path.toString());
Like the Dispatch actions (see Tips #2 and #3), this action needs to get the
name of the parameter from the mapping's parameter property. Here's the
corresponding mapping:
<action 
path="/menu/Find"
type="org.apache.scaffold.struts.ParameterAction"
name="menuForm"
validate="false"
parameter="keyValue">
<forward 
name="title"  
path="/do/find/Title?title="/> 
<forward 
name="author"  
path="/do/find/Author?creator="/> 
<forward 
name="content"  
path="/do/find/Content?content="/> 
<forward 
name="article"  
path="/do/article/View?article="/> 
</action>

You may note that the action uses a form-bean called "menuForm". This is a
simple bean with properties common to many menu items. Here's the source:
private String keyName = null;
public String getKeyName() {
return this.keyName;
    }
public void setKeyName(String keyName) {
this.keyName = keyName;
    }

private String keyValue = null;
public String getKeyValue() {
return this.keyValue;
    }
public void setKeyValue(String keyValue) {
this.keyValue = keyValue;
    }

private String dispatch = null;
public String getDispatch() {
return this.dispatch;
    }
public void setDispatch(String dispatch) {
this.dispatch = dispatch;
    }

The version of the MenuForm in the Scaffold package includes some other
convenience properties, but these three are the important ones. 
Of course, your form can still use all the properties it needs. They all go
into the request and stay there for the duration. When one of the standard
action forwards the request, all the original parameters go with it. You can
populate as many ActionForms as you like from the same request. When the
request arrives at the mapping for the target action, whatever form-bean it
uses is populated normally. 
So far, we've looked at using the RelayAction to select between different
submit buttons or menu selections, and the ParameterAction to append a value
to a query string. There is one more action like this in our repertoire, the
FindForwardAction. 
The RelayAction relies on there being a parameter with a known name in a
request. For example, [dispatch=save]. It looks for the parameter named
"dispatch", then looks for a forward named "save".  The FindForwardAction is
even more dynamic. It runs through all the parameter names and checks to see
if any are also the name of a forward.  If so, it returns the matching
ActionForward.
This can be a good way to match multiple submit buttons without using
JavaScript to set the dispatch property. If you have buttons named save,
create, and delete, and forwards also named save, create, and delete, the
FindFowardAction will automatically match one with the other. 
JSP:
<html:submit name="save">SAVE</html:submit>
<html:submit name="create">SAVE AS NEW</html:submitl> 
<html:submit name="delete">DELETE</html:submit>
config:
<action 
name="articleForm" 
path="/do/article/Submit"  
type="org.apache.scaffold.FindForwardAction" 
            ... >
<forward 
name="create"  
path="/do/article/Create"/> 
<forward 
name="save"  
path="/do/article/Store"/> 
<forward 
name="delete"  
path="/do/article/Recycle"/> 
</action>
The source for the FindForwardAction is a little more complicated than the
others, but is at least concise. 
for (int i=0; i<forwards.length; i++) {
if (request.getParameter(forwards[i])!=null) {
// Return the required ActionForward instance return
mapping.findForward(forwards[i]);
             }
         }

return null;
The only caveat here is that you have to manage your forward and control
names more carefully. The FindForwardAction will check all the parameters on
the form with all the available forwards, and the first one it finds wins.
So if any of your control names match any of your forward names, it might
come up with an unexpected match. 
This can be useful when you cannot add a dispatch property to a form, or
cannot use JavaScript to set the dispatch property. But the RelayAction may
be preferred since it is more "deterministic". 
Using these techniques together can fill a surprising number of your menuing
needs and keeps all the flow control within the Struts configuration. 
HTH, Ted.
Credits. The FindForwardAction is based on code by Dmitri Valdin. 
Struts Tips are released twice weekly on the MVC-Programmers List. 
To subscribe, visit BaseBean Engineering <http://www.basebeans.com>. 
Archives of the past columns are available at JGuru 
<http://jguru.com/faq/subtopic.jsp?topicID=893704>
About Ted. Ted Husted is an active Struts Committer. He also moderates the
Struts mailing list and the JGuru Struts FAQ.
Copyright Ted Husted 2002. All rights reserved. 

###
_______________________________________________
MVC-Programmers mailing list
[EMAIL PROTECTED]
http://www.basebeans.com:8081/mailman/listinfo/mvc-programmers
_______________________________________________
MVC-Programmers mailing list
[EMAIL PROTECTED]
http://www.netbean.net/mailman/listinfo/mvc-programmers

Reply via email to