Cooperating Tags

Sometimes it is useful having tags meant to work together. For instance we could like to have a sort of Menu tag that refers to MenuItem tags, so that we can write in a JSP page:
<st:menu> 
    <st:menuItem value="Dogs" />
    <st:menuItem value="Cats" />
    <st:menuItem value="Mice" />
</st:menu>
We want establish a sort of communication among them, so that an internal tag could access methods of the containing tag, and even modify it.

We can do that using the getParent() method provided by the SimpleTag (or Tag, if you are working with Classic Tags) interface.

Let's create a couple of Simple Tags that would let us testing the JSP code we wrote above. Our menu tag would have a private list of string, and would make available to the menuItem tag a method to add a value to it.

We expect the inner tags to do their job, and we, from the menu doTag() we are going to show the list that, after invoking the JspBody should contain the values set by the internal tags:
package ch10;

import java.io.IOException;
import java.util.ArrayList;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class MenuTag extends SimpleTagSupport {
    private ArrayList<String> items = new ArrayList<String>();

    // package visibility: internal use only!
    void addMenuItem(String item) {
        items.add(item);
    }

    @Override
    public void doTag() throws JspException, IOException {
        JspWriter out = getJspContext().getOut();
        out.print("Menu tag< br />");

        if(getJspBody() != null)
            getJspBody().invoke(null);

        out.println("< br />");
        out.print("Items are: " + items + "< br />");
    }
}
To be this tag available to our JSP page, we declare it in our TLD:
<tag>
    <description>Menu Tag</description>
    <name>menu</name>
    <tag-class>ch10.MenuTag</tag-class>
    <body-content>scriptless</body-content>
</tag>
All the rest of the job is done by the internal node class that, in its doTag() method, check if the tag has a parent and if it is of the expected type. If so, it calls the method we have expressly created for this scope:
package ch10;

import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.JspTag;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class MenuItemTag extends SimpleTagSupport {
    private String value;

    public void setValue(String value) {
        this.value = value;
    }

    @Override
    public void doTag() throws JspException, IOException {
        JspWriter out = getJspContext().getOut();
        out.print("Menu Item tag: " + value + "<br />");

        JspTag tag = getParent();
        if(tag != null && tag instanceof MenuTag)
            ((MenuTag)tag).addMenuItem(value);
        else if(tag == null)
            out.print("no parent for this tag<br />");
        else
            out.print("parent is not a MenuTag");
    }
}
We can test it leaving a MenuItem tag free, or putting it in an unexpected tag:
<st:menuItem value="Horses" />
<st:simple2><st:menuItem value="Hippo" /></st:simple2>
Just remember to put in the JSP page the required taglib directive:
<%@ taglib prefix="st" uri="simpleTags" %>
More on tag handlers in chapter ten of Head First Servlet and JSP. A fun and interesting book, indeed.

No comments:

Post a Comment