问题
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.
回答1:
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.
回答2:
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);
}
来源:https://stackoverflow.com/questions/20817230/jsp-make-a-reusable-code-tag-macro-and-use-it-on-the-same-page