Adding parameters from a map to a URL tag

12,076

Solution 1

The Parameter tag populates parameters only to its direct antecessor. Wrappings an iterator tag around the parameter tag has no effect. :) To solve this you can easily write an alternative parameter tag wich can use a map direclty The tag may look like this.

package my.taglibs;

import java.io.Writer;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.components.Component;
import org.apache.struts2.views.jsp.ComponentTagSupport;
import com.opensymphony.xwork2.util.ValueStack;

public class ParamTag extends ComponentTagSupport {

    private String map;
    private static final long serialVersionUID = 2522878390854066408L;
    Log log = LogFactory.getLog(ParamTag.class);

    @Override
    public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
        return new Param(stack);
    }

    @Override
    protected void populateParams() {
        super.populateParams();

        Param param = (Param) component;
        param.setMap(map);
    }

    public void setMap(String map) {
        this.map = map;
    }

    public class Param extends Component {

        private String map; 

        public Param(ValueStack stack) {
            super(stack);
        }

        @Override
        public boolean end(Writer writer, String body) {
            Component component = findAncestor(Component.class);
            if (this.map == null) {       
                log.warn("Attribute map is mandatory.");
                return super.end(writer, "");
            }
            Object o = findValue(this.map); //find map in vs
            if(o == null) {
                log.warn("map not found in ValueStack");
                return super.end(writer, "");
            }
            if(!(o instanceof Map)) {
                log.warn("Error in JSP. Attribute map must evaluate to java.util.Map. Found type: " + o.getClass().getName());
                return super.end(writer, "");
            }

            component.addAllParameters((Map) o);
            return super.end(writer, "");
        }

        public void setMap(String map) {
            this.map = map;
        }
    }
}

And you need a corresponding tld-entry

<tag>
    <description><![CDATA[Parametrize other tags]]></description>
    <name>param</name>
    <tag-class>my.taglibs.ParamTag</tag-class>
    <body-content>JSP</body-content>
    <attribute>
        <name>map</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>
    <dynamic-attributes>false</dynamic-attributes>
</tag>

Solution 2

Maybe you can use JSTL tags like this

 <c:url var="uri" value="${namespace}/${action}.action">                
                    <c:forEach items="${parameters}" var="p">
                        <c:param name="${p.key}" value="${p.value}"/>
                    </c:forEach>
</c:url>
<a href="${uri}">Your Link</a>

I know there is some hesitation to mix EL with OGNL etc but this works...

Share:
12,076
Peter Kelley
Author by

Peter Kelley

Architecture Director working for Fujitsu Australia in Canberra ACT. I play modern boardgames and dabble in Groovy web applications in my spare time.

Updated on June 04, 2022

Comments

  • Peter Kelley
    Peter Kelley almost 2 years

    I want to add a variable list of parameters to a Struts2 URL tag. I have a map of the parameters (name value pairs) in an object in the session. I'm struggling to find a good approach to this. Here is the relevant JSP code:

        <s:iterator value="%{#session['com.strutsschool.interceptors.breadcrumbs']}" status="status">
        <s:if test="#status.index > 0">
            &#187;
        </s:if>
        <s:url id="uri" action="%{action}" namespace="%{nameSpace}">
                <s:param name="parameters" value="%{parameters}"/>
        </s:url>
        <nobr><s:a href="%{uri}"><s:property value="displayName"/></s:a></nobr>
    </s:iterator>
    

    The parameters variable is a Map that contains the params. This, of course does not work but I cannot see a way to approach this at the moment. I'm thinking at the moment that I might need a custom freemarker template for this. Can anyone suggest a better way?

  • jjmontes
    jjmontes almost 13 years
    +1 for the short, concise snippet and the adequate comment on how EL and OGNL don't fit sometimes. In any case, I don't see a problem with that.