Struts HTML Tags


Struts HTML tags are useful for generating HTML markup. The Struts HTML tag library defines tags for generating HTML forms, textboxes, check boxes, drop downs , radio buttons, submit buttons and so on. You have already used some of these in Chapter 3. We will look at other important html tags not covered there.

Modifying the Base Tag

This tag renders the < base href= ‚ ‚½ > html tag pointing to the absolute location of the JSP containing the tag as follows :

 <base href=http://localhost:8080/App1/abc/CustomerDetail.jsp/> 

This can be problematic at times. Assume that the JSP itself is present somewhere down in a hierarchy of directories. Also the images directory will be generally at the top level in a web application (See the WAR structure in Figure 3.3). Since the base href is referring to the absolute location of the JSP, the URL for the images might look like ‚“ ../../images/banner.jsp ‚½. Three reasons why this is not a good idea:

  1. Referring to a same image with different URLs depending on from which JSP it is called is error prone and creates a lot of confusion.

  2. If the JSP is moved from one folder to another (which is not uncommon), every URL in the page should be inspected and changed if needed. Not a great idea.

  3. Even though the Servlet specification encourages the idea of bundling the images JavaScript and other static resource along with the WAR, it is not a good idea in practice. It is a norm to deploy the static resources separately so that the web server serves these documents instead of the servlet container.

  4. When using frameworks such as Tiles (Chapter 7), there is no concept of a single JSP. There is a single layout that aggregates the JSPs

The solution is to modify the Base Tag itself so that the output is:

 <base href=http://localhost:8080/App1 /> 

Now, the URL of the image is always a constant no matter which JSP it is used in. Another advantage of this arrangement is that a directory named App1 can be created on the web server to contain the static resources and the images with no impact on the image URLs. With this background let us get started on modifying the BaseTag .

Consider a URL http://localhost:8080/App1/cust/CustomerDetail.jsp. This is generated as the output of the BaseTag . It can be dissected into:

request.getScheme() ( http:// ),

request.getServerName() ( localhost ),

request.getServerPort() ( 8080 ) and

request.getRequestURI() ( App1/customer/CustomerDetails.jsp ).

The desired output for the BaseTag is http://localhost:8080/App1. This can be dissected into

request.getScheme() ( http:// ),

request.getServerName() ( localhost ),

request.getServerPort() ( 8080 ) and

request.getContextPath() ( App1 ).

There you go! This is what we want to output from our version of BaseTag . Let us call this MyBaseTag . Listing 6.1 shows doStartTag() method from MyBaseTag .

Listing 6.1: MybankBaseTag ‚ Customized BaseTag
 public class MybankBaseTag extends BaseTag {        public int doStartTag() throws JspException {       HttpServletRequest request =                (HttpServletRequest) pageContext.getRequest();       String baseTag = renderBaseElement(request.getScheme(), request.getServerName(),         request.getServerPort(),request.getContextPath());       JspWriter out = pageContext.getOut();       try {          out.write(baseTag);       } catch (IOException e) {          pageContext.setAttribute(Globals.EXCEPTION_KEY, e,                    PageContext.REQUEST_SCOPE);          throw new JspException(e.toString());       }       return EVAL_BODY_INCLUDE;    } .. } 
 

Form Tag

Another Tag that deserves extra attention is the FormTag . You have learnt about the working of this tag in Chapter 2 and used it in Chapter 3. At that point, we looked at only one attribute of this tag ‚ the action attribute.

It also has a set of attributes based on JavaScript events. For instance, the onreset and onsubmit attributes do exactly what their JavaScript equivalents do; they invoke the corresponding JavaScript event handler functions. The JavaScript event based attributes is not limited to just the FormTag . In fact all the tags in HTML Tag library have similar features.

Another attribute of interest is the enctype . Normally you don ‚ t have to set the enctype . When you are uploading files however, the value of enctype should be set to multipart/form-data . More details await you in the section on FileTag .

FileTag

FileTag lets you select file to upload from the HTML page. When you are uploading files, the value of enctype (on the FormTag ) should be set to multipart/form-data . The FileTag in its simplest format, generates an output of < input type= ‚½file ‚½ name = ‚½xyz ‚½ value ‚½abc ‚½ / >. This results in the rendering of a text field for entering the file name and a Browse button as shown in the figure below.

On clicking the browse button a file selection dialog box appears. The selected file is uploaded when the form is submitted. In the JSP, the FileTag is used as < html:file property= ‚½uploadFile ‚½/ >. The uploadFile is a JavaBeans property in the ActionForm. Struts mandates the type of this property to be org.apache.struts.upload.FormFile . FormFile is an interface with methods to get the InputStream for the uploaded file. For more details refer to the example web application named struts-upload.war in the webapps directory of wherever you installed Struts.

Smart Checkbox ‚ The state aware checkbox

Consider a HTML form containing a checkbox in a JSP as follows:

 <html:form action="/submitCustomerForm">         <html:text property="firstName" />         <html:checkbox property="agree" />         <html:submit>Submit</html:submit>     </html:form> 

In addition to the usual text field, it has a checkbox that Customer checks to indicate he agrees with the terms and conditions. Assume that the associated ActionForm has validate() method checking if the firstName is not null. If the first name is not present, then the user gets the same page back with the error displayed. The user can then submit the form again by correcting the errors. Further assume that the associated ActionForm is stored in session scope. Now starts the fun.

  1. First, submit the form by checking the checkbox but leaving the firstName blank. The form submission request looks like this:

     http://localhost:8080/App1/submitCustomer.do?                         firstName=""&agree="true" 

    The ActionForm is created in the session with blank firstName and agree attribute set to true (Checkbox is mapped to Boolean attributes in ActionForm).

  2. Since the firstName is blank, the user gets the same page back. Now fill in the firstName but uncheck the agree checkbox. The form submission request looks like this:

     http://localhost:8080/App1/submitCustomer.do?firstName="John" 

    Note that the agree request parameter is missing. This is nothing unusual. According to the HTTP specification, if a checkbox is unchecked, then it is not submitted as request parameter. However since the ActionForm is stored in the Session scope, we have landed in a problem. In our case, Struts retrieves the ActionForm from Session and sets the firstName to ‚“ John ‚½. Now the ActionForm has the firstName=John and agree=true , although you intended to set the agree to be false.

The Smart Checkbox we are about to present is the solution to this problem. This solution uses JavaScript and it works as expected only if your target audience enables JavaScript in their browser. The solution is as follows:

  • Define the ActionForm as usual with the Boolean property for checkbox.

  • Define a new class SmartCheckboxTag by extending the CheckboxTag in org.apache.struts.taglib.html package and override the doStartTag() . In the doStartTag() , do the following:

  • Render a checkbox with name ‚“ agreeProxy ‚½, where agree is the name of the boolean property in ActionForm.

  • Render a hidden field with the name agree.

  • Define an inline JavaScript within the <script> block as follows. Substitute appropriate values into [property] and [formName].

     <script>        function handle" + [property] + "Click(obj) {          if (obj.checked == true) {           document.form.[formName]."                     + [property] + ".value = 'true';          } else {             document.form.[formName]."                     + [property] + ".value = 'false';          }       }     </script> 
  • Invoke the above JavaScript function for the onclick event of the checkbox.

The crux of this solution is to invoke a JavaScript function on clicking (check or uncheck) the checkbox to appropriately set the value of a hidden field. The hidden field is then mapped to the actual property in ActionForm. If you can ensure that your target audience has JavaScript enabled, this solution works like a charm !

Many might classify this solution as a hack, but the truth is there is no elegant solution for this problem. Where applicable and feasible you can adopt this solution. If you are unsure about your target audience or deploying the application into the public domain, never use this solution. It is impossible to predict the environment and behavior of an Internet user.

Using CSS with Struts HTML Tags

Cascading Style Sheets (CSS) is the mechanism used by page authors to centralize and control the appearance of a HTML page. Some of the uses of CSS are:

  • Text formatting, indentation and fonts

  • Add background color to text, links and other HTML tags

  • Setting table characteristics such as styled borders, widths and cell spacing.

CSS allows the page author to make changes to formatting information in one location and those changes immediately reflect on all pages using that stylesheet thus resulting in application with consistent appearance with the least amount of work ‚ in other words a highly maintainable application.

The developers of Struts tags had this in mind and thus most of the HTML tags support the usage of stylesheet in the form of styleClass and style attributes. The styleClass refers to the CSS stylesheet class to be applied to the HTML element and the style attribute refers to the inline CSS styles in the HTML page. You can use either but styleClass is most frequently used.

Enhancing the error display with customized TextTag

You already know how to validate an ActionForm and display the error messages to the user. This approach works great so long as the forms are small enough and the resulting error display fits into the viewable area without scrolling. If the forms are larger, it is a hassle for the user to scroll down and check for the messages. We address this usability challenge with an error indicator next to the field as shown in the figure below. In addition to the quick visual impact, the error indicator can also provide more information such as displaying a popup box with errors for that field in a JavaScript enabled browser thus enriching the user experience.

There are many ways to implement this. One simple way is to extend the TextTag class and override the doStartTag() method. The doStartTag() from the Struts TextTag generates the < input type= ‚½text ‚½ name= ‚½.. ‚½ >. The subclass of the Struts TextTag has to then add an image next to it when there is ActionError (s) associated with the input field. Listing 6.2 shows the implementation with the above approach. The new tag called MyTextTag is used in the JSP as follows:

 <mytags:mytext property="." errorImageKey="img.error.alert" /> 
Listing 6.2: TextTag with built-in error indicator
 public class MyTextTag extends TextTag {    private String errorImageKey;    public int doStartTag() throws JspException {       int returnValue = super.doStartTag();                ActionErrors errors = RequestUtils.getActionErrors(pageContext, this.property);       if ((errors != null) && ! errors.isEmpty()) {                  String imageSrc = RequestUtils.message(pageContext,                                           getBundle(),                                           getLocale(),                                          this.errorImageKey);          if (imageSrc != null) {             StringBuffer imageResults = new StringBuffer();             imageResults.append("<img src=\"");             imageResults.append(imageSrc);             imageResults.append("\"");             // Print the image to the output writer             ResponseUtils.write(pageContext,                               imageResults.toString());          }       }       return returnValue;    }       ...       ...    public void release() {       super.release();       errorImageKey = null;    } } 
 

The errorImageKey is the key to get the name of the error image from the resource bundle. In the doStartTag() method, a check is performed if the text field has any associated errors. If there are no errors, no extra processing is done. However if there are errors, the errorImageKey is used to retrieve the image source and a < img src= ‚½ ‚ ‚½ > markup is constructed alongside the text tag. There are other ways of implementing this feature. One of them is to develop a separate custom tag to generate the error indicator.

Further customizations can also be performed to pop up Java Script alerts to show the error, if needed. This requires communication between Java and JavaScript. Sounds complex right. It is easier than you think! You can achieve this in three steps. All you need is a basic understanding of JavaScript.

First, create a JavaScript function as shown in Listing 6.3. This function simply creates a JavaScript data structure and adds individual ActionError to a JavaScript object called errorMessageArray . An array is created for every for form field to hold multiple error messages.

Listing 6.3: JavaScript function to add ActionError into a JavaScript data structure
 function addActionError(window, formFieldName, errorMessage) {     if (! window.errorMessageArray)  {        window.errorMessageArray = new Object();    }     var value = window.errorMessageArray[formFieldName];     if (typeof(value) == "  undefined  ") {      window.errorMessageArray[field] = new Array();       window.errorMessageArray[formFieldName][0] = errorMessage ;     }     else {       var length =         window.errorMessageArray[formFieldName].length;       window.errorMessageArray[formFieldName][length] =                                              errorMessage;     } } 
 

Second, create your own Errors tag by extending the ErrorsTag from Struts. This JavaScript function is invoked repetitively from the ErrorsTag ‚ s doStartTag() method for every ActionError in ActionErrors . Listing 6.4 shows the doStartTag() method for the MyErrorsTag . As usual the method first invokes the super.doStartTag() to write the ActionErrors as locale specific error messages to the output stream. It then invokes the JavaScript function addActionError() inline with the rest of HTML for every ActionError . The JavaScript invocation is made inline by using < script > and < /script > demarcations. At the end of this method, every ActionError associated with the form fields is added to the JavaScript data structure ( errorMessageArray ). Any JavaScript code in the page can now access the data structure to do whatever it likes.

Listing 6.4: MyErrorsTag invoking the JavaScript functions
 public class MyErrorsTag extends ErrorTag {   public int doStartTag() throws JspException {     int returnValue = super.doStartTag();  //Retrieve the ActionErrors  ActionErrors errors = RequestUtils.getActionErrors(pageContext, Globals.ERROR_KEY);     StringBuffer results = new StringBuffer();     results.append("<script>");  //Retrieve all the form field names having ActionError  Iterator properties = errors.properties();     String formFieldName = null;     while (properties.hasNext()) {       formFieldName = (String) properties.next();       if (formFieldName.equals(ActionMessages.GLOBAL_MESSAGE))        continue;  //Retrieve all ActionError per form field  Iterator reports = errors.get(formFieldName);       String message = null;       while (reports.hasNext()) {         ActionError report = (ActionError) reports.next();         message = RequestUtils.message(pageContext, bundle,                     locale, report.getKey(), report.getValues());  //Invoke the JavaScript function for every ActionError  results.append("addActionError(window,\"" +                                   formFieldName + "\",\"" +                                   message + "\");\n");       }     }     results.append("</script>");     ResponseUtils.write(pageContext, results.toString());         return returnValue;   }      ... } 
 

Finally the error messages in the JavaScript data structure (added by MyErrorsTag ) have to be displayed when clicking on the error indicator. This can be done with a simple JavaScript function as shown in Listing 6.5. The displayAlert() function iterates over the error messages for the given form field. This function has to be invoked on the onclick JavaScript event of the error indicator image.

Listing 6.5: JavaScript function to display alert with ActionError messages
 function displayAlert(window, formFieldName) {   var length = window.errorMessageArray[formFieldName].length;   var aggregateErrMsg = "";   for(var i = 0;  i < length; i++) {     aggregateErrMsg = aggregateErrMsg +             window.errorMessageArray[formFieldName][i];   }   alert(aggregateErrMsg); } 
 

The recommended way to use ImgTag

The ImgTag is used to render a HTML < img > element such as follows:

 <img src=images/main.gif alt=The Main Image/> 

If you are wondering why you need a Struts tag for such a simple HTML tag, consider this. Sometimes, the images actually spell out the actual English word. Users worldwide access your application. You want to localize the images displayed to them. You also want the alt text on your images to be internationalized. How do you do this without adding the big and ugly if-else block in your JSPs? The answer is to use the ImgTag . With ImgTag , the actual image ( src ) and the alternate text ( alt ) can be picked from the Message Resources. You can easily setup different Resource Bundles for different Locales and there you have it. Your images are internationalized without any extra effort. Even if you are not internationalizing the effort is well worth it. JSPs can remain untouched when the image is changed. The usage of the ImgTag is as follows:

 <html:img srcKey=image.main altKey=image.main.alttext /> 

There are many more attributes in ImgTag and you can find them in the Struts documentation.




Struts Survival Guide. Basics to Best Practices
Struts Survival Guide: Basics to Best Practices (J2ee Survival Series)
ISBN: 0974848808
EAN: 2147483647
Year: 2004
Pages: 96

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net