Is there any way to make some sort of parametrized macro on one JSP page and reuse it few times on the same page. JSP tags could be used but I would have to make one file per tag.
I've been wanting this functionality for years, and after googling yet again, I wrote my own. I think tag / jsp files & custom tag classes are great, but are often overkill for simple one-offs like you describe.
This is how my new "macro" tag now works (here used for simple html rendering of sortable table headers):
<%@ taglib prefix="tt" uri="/WEB-INF/tld/tags.tld" %>
<!-- define a macro to render a sortable header -->
<tt:macro id="sortable">
    <th class="sortable">${headerName}
        <span class="asc" >↑</span>
        <span class="desc">↓</span>
    </th>
</tt:macro>
<table><thead><tr>
    <!-- use the macro for named headers -->
    <tt:macro id="sortable" headerName="Name (this is sortable)" />
    <tt:macro id="sortable" headerName="Age (this is sortable)" />
    <th>Sex (not sortable)</th>
    <!-- etc, etc -->
In /WEB-INF/tld/tags.tld, I added:
<tag>
    <name>macro</name>
    <tag-class>com.acme.web.taglib.MacroTag</tag-class>
    <body-content>scriptless</body-content>
    <attribute>
        <description>ID of macro to call or define</description>
        <name>id</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>
    <dynamic-attributes>true</dynamic-attributes>
</tag>
And, finally, the Java tag class:
public class MacroTag
    extends SimpleTagSupport implements DynamicAttributes
{
    public static final String PREFIX = "MacroTag_";
    private boolean bodyless = true;
    private String id;
    private Map<String, Object> attributes = new HashMap<String, Object>();
    @Override public void setJspBody(JspFragment jspFragment) {
        super.setJspBody(jspFragment);
        getJspContext().setAttribute(PREFIX + id, jspFragment, PageContext.REQUEST_SCOPE);
        bodyless = false;
    }
    @Override public void doTag() throws JspException, IOException {
        if (bodyless) {
            JspFragment jspFragment = (JspFragment) getJspContext().getAttribute(PREFIX + id, PageContext.REQUEST_SCOPE);
            JspContext ctx = jspFragment.getJspContext();
            for (String key : attributes.keySet())
                ctx.setAttribute(key, attributes.get(key));
            jspFragment.invoke(getJspContext().getOut());
            for (String key : attributes.keySet()) {
                ctx.removeAttribute(key);
            }
        }
    }
    public void setId(String id) {
        this.id = id;
    }
    @Override public void setDynamicAttribute(String uri, String key, Object val) throws JspException {
        attributes.put(key, val);
    }
}
The implementation is pretty basic. If the tag has a body, we assume we're defining a macro, and we store that JspFragment. Otherwise, we assume we're calling a macro, so we look it up, and copy any dynamic attributes into its context so it be properly parameterized, and render it into the calling output stream.
Crazy this isn't built into JSP.
I tried the solution from Johnny and found, that if you use the macro multiple times, there is a bug.
you have to remove the attributes from the page context after renering
jspFragment.invoke(getJspContext().getOut());
for (String key : attributes.keySet()) {
    ctx.removeAttribute(key);
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With