我有一个bean列表,每个bean都有一个属性,它本身就是一个电子邮件地址列表。
<c:forEach items="${upcomingSchedule}" var="conf">
<div class='scheduled' title="${conf.subject}" id="scheduled<c:out value="${conf.id}"/>">
...
</div>
</c:forEach>
这会在List中为每个bean呈现一个<div>
。
对于子列表,我希望能够做的是连接列表中的每个条目以形成单个String,以显示为<div>
的{{1}} {{{ 1}}属性。为什么?因为我们使用javascript库(mootools)将此title
转换为浮动工具提示,并且库将<div>
转换为工具提示的文本。
所以,如果title
是“主题”,最终我希望${conf.subject}
的{{1}}为“主题:blah@blah.com,blah2 @ blah2.com等,“,包含子列表的所有电子邮件地址。
如何使用JSP EL执行此操作?我试图远离jsp文件中的scriptlet块。
答案 0 :(得分:16)
执行此操作的“干净”方法是使用函数。由于JSTL join
函数不适用于Collection
,因此您可以编写自己的函数而不会遇到太多麻烦,并且可以在整个地方重复使用它,而不是剪切并粘贴一大块循环代码。
您需要功能实现和TLD才能让您的Web应用程序知道在哪里找到它。将它们放在一起放在JAR中,然后放入WEB-INF / lib目录。
这是一个大纲:
COM / X /标签库/型芯/ StringUtil.java
package com.x.taglib.core;
public class StringUtil {
public static String join(Iterable<?> elements, CharSequence separator) {
StringBuilder buf = new StringBuilder();
if (elements != null) {
if (separator == null)
separator = " ";
for (Object o : elements) {
if (buf.length() > 0)
buf.append(separator);
buf.append(o);
}
}
return buf.toString();
}
}
META-INF / X-c.tld:
<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0">
<tlib-version>1.0</tlib-version>
<short-name>x-c</short-name>
<uri>http://dev.x.com/taglib/core/1.0</uri>
<function>
<description>Join elements of an Iterable into a string.</description>
<display-name>Join</display-name>
<name>join</name>
<function-class>com.x.taglib.core.StringUtil</function-class>
<function-signature>java.lang.String join(java.lang.Iterable, java.lang.CharSequence)</function-signature>
</function>
</taglib>
虽然TLD有点冗长,但对于使用JSP的任何开发人员来说,了解自己的方法是一项很好的技能。而且,既然您选择了像JSP这样的标准进行演示,那么您很有可能拥有可以帮助您的工具。
与向底层模型添加更多方法的替代方法相比,此方法具有许多优势。此函数可以编写一次,并在任何项目中重用。它适用于封闭源的第三方库。可以在不同的上下文中支持不同的分隔符,而不会使用每种方法的新方法污染模型API。
最重要的是,它支持视图和模型控制器开发角色的分离。这两个领域的任务通常由不同的人在不同的时间执行。保持这些层之间的松散耦合最小化了复杂性和维护成本。即使是在演示文稿中使用不同的分隔符等微不足道的更改,也需要程序员修改库,但是系统非常昂贵且繁琐。
StringUtil
类无论是否作为EL函数公开都是相同的。唯一需要的“额外”是TLD,这是微不足道的;一个工具可以很容易地生成它。
答案 1 :(得分:8)
想出一个有点脏的方法:
<c:forEach items="${upcomingSchedule}" var="conf">
<c:set var="title" value="${conf.subject}: "/>
<c:forEach items="${conf.invitees}" var="invitee">
<c:set var="title" value="${title} ${invitee}, "/>
</c:forEach>
<div class='scheduled' title="${title}" id="scheduled<c:out value="${conf.id}"/>">
...
</div>
</c:forEach>
我只是反复使用<c:set>
,引用它自己的值,来追加/连接字符串。
答案 2 :(得分:3)
${fn:join(array, ";")}
http://java.sun.com/products/jsp/jstl/1.1/docs/tlddocs/fn/join.fn.html
答案 3 :(得分:1)
如果你的子列表是一个ArrayList,你可以这样做:
<div class='scheduled' title="${conf.subject}: ${conf.invitees}" id="scheduled${conf.id}">
你几乎可以获得所需的东西。
唯一的区别是标题是: “主题:[blah@blah.com,blah2@blah2.com等]”。
也许对你来说足够好。
答案 4 :(得分:0)
我认为这就是你想要的:
<c:forEach var="tab" items="${tabs}">
<c:set var="tabAttrs" value='${tabAttrs} ${tab.key}="${tab.value}"'/>
</c:forEach>
在这种情况下,我有一个带有选项卡ID(键)和URL(值)的哈希映射。之前未设置tabAttrs变量。所以它只是将值设置为tabAttrs的当前值(''开始)加上键/值表达式。
答案 5 :(得分:0)
将字符串放在服务器的var旁边,如下所示:
<c:forEach items="${upcomingSchedule}" var="conf">
<div class='scheduled' title="${conf.subject}"
id="scheduled${conf.id}">
...
</div>
</c:forEach>
太晚了!!!
答案 6 :(得分:0)
标签库的实现方式似乎已经发生了很大的变化,因为这个答案最初发布,所以我最终做了一些重大改变以使工作正常。我的最终结果是:
标记库文件:
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd">
<tlib-version>1.0</tlib-version>
<short-name>string_util</short-name>
<uri>/WEB-INF/tlds/string_util</uri>
<info>String Utilities</info>
<tag>
<name>join</name>
<info>Join the contents of any iterable using a separator</info>
<tag-class>XXX.taglib.JoinObjects</tag-class>
<body-content>tagdependent</body-content>
<attribute>
<name>iterable</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.Iterable</type>
</attribute>
<attribute>
<name>separator</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>java.lang.String</type>
</attribute>
</tag>
<tag>
<name>joinints</name>
<info>Join the contents of an integer array using a separator</info>
<tag-class>XXX.taglib.JoinInts</tag-class>
<body-content>tagdependent</body-content>
<attribute>
<name>integers</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.Integer[]</type>
</attribute>
<attribute>
<name>separator</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>java.lang.String</type>
</attribute>
</tag>
</taglib>
JoinInts.java
public class JoinInts extends TagSupport {
int[] integers;
String separator = ",";
@Override
public int doStartTag() throws JspException {
if (integers != null) {
StringBuilder buf = new StringBuilder();
if (separator == null) {
separator = " ";
}
for (int i: integers) {
if (buf.length() > 0) {
buf.append(separator);
}
buf.append(i);
}
try {
pageContext.getOut().print(buf);
} catch (IOException ex) {
Logger.getLogger(JoinInts.class.getName()).log(Level.SEVERE, null, ex);
}
}
return SKIP_BODY;
}
@Override
public int doEndTag() throws JspException {
return EVAL_PAGE;
}
public int[] getIntegers() {
return integers;
}
public void setIntegers(int[] integers) {
this.integers = integers;
}
public String getSeparator() {
return separator;
}
public void setSeparator(String separator) {
this.separator = separator;
}
}
使用它:
<%@ taglib prefix="su" uri="/WEB-INF/tlds/string_util.tld" %>
[new Date(${row.key}), <su:joinints integers="${row.value}" separator="," />],
答案 7 :(得分:0)
您可以使用EL 3.0 Stream API。例如,如果您有一个字符串列表,
<div>${stringList.stream().reduce(",", (n,p)->p.concat(n))}</div>
如果你有一个ex的对象列表。 Person(firstName,lastName)并且你只想连接它们的一个属性(ex firstName)你可以使用map,
<div>${personList.stream().map(p->p.getFirstName()).reduce(",", (n,p)->p.concat(n))}</div>
在你的情况下,你可以使用类似的东西(删除最后的&#39;,也是),
<c:forEach items="${upcomingSchedule}" var="conf">
<c:set var="separator" value=","/>
<c:set var="titleFront" value="${conf.subject}: "/>
<c:set var="titleEnd" value="${conf.invitees.stream().reduce(separator, (n,p)->p.concat(n))}"/>
<div class='scheduled' title="${titleFront} ${titleEnd.isEmpty() ? "" : titleEnd.substring(0, titleEnd.length()-1)}" id="scheduled<c:out value="${conf.id}"/>">
...
</div>
</c:forEach>
小心! EL 3.0 Stream API在Java 8 Stream API之前完成,并且与此不同。他们不能同时使用api,因为它会破坏向后兼容性。