agregar - taglib java
Concatenar cadenas en JSP EL? (8)
Tengo una Lista de frijoles, cada uno de los cuales tiene una propiedad que en sí misma es una Lista de direcciones de correo electrónico.
<c:forEach items="${upcomingSchedule}" var="conf">
<div class=''scheduled'' title="${conf.subject}" id="scheduled<c:out value="${conf.id}"/>">
...
</div>
</c:forEach>
Esto representa un <div>
por bean en la Lista.
Para la sublista, lo que me gustaría hacer es concatenar cada una de las entradas en la lista para formar una sola Cadena, que se mostrará como parte del atributo de title
del <div>
. ¿Por qué? Porque estamos utilizando una biblioteca de JavaScript (mootools) para convertir este <div>
en una sugerencia de herramienta flotante, y la biblioteca convierte el title
en el texto de la información sobre herramientas.
Entonces, si ${conf.subject}
era "Subject", al final me gustaría que el title
de <div>
sea "Subject: [email protected], [email protected], etc.", que contiene todos las direcciones de correo electrónico de la sub-lista.
¿Cómo puedo hacer esto usando JSP EL? Estoy tratando de evitar poner bloques scriptlet en el archivo jsp.
Entendí una forma algo sucia de hacer esto:
<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>
Solo uso <c:set>
repetidamente, haciendo referencia a su propio valor, para anexar / concatenar las cadenas.
Podrías usar esto? Parece que quiere una matriz en lugar de una lista ...
${fn:join(array, ";")}
http://java.sun.com/products/jsp/jstl/1.1/docs/tlddocs/fn/join.fn.html
Si su sublista es una ArrayList y hace esto:
<div class=''scheduled'' title="${conf.subject}: ${conf.invitees}" id="scheduled${conf.id}">
obtienes casi lo que necesitas
La única diferencia es que el título será: "Asunto: [[email protected], [email protected], etc.]".
Tal vez puede ser lo suficientemente bueno para ti.
La forma "limpia" de hacer esto sería usar una función. Como la función de join
JSTL no funcionará en una Collection
, puede escribir la suya sin demasiados problemas y volver a utilizarla en lugar de cortar y pegar una gran parte del código de bucle.
Necesita la implementación de la función y un TLD para que su aplicación web sepa dónde encontrarla. Colóquelos en un JAR y suéltelos en su directorio WEB-INF / lib.
Aquí hay un resumen:
com / x / taglib / core / 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 / xc.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>
Si bien el TLD es un poco detallado, conocer uno es una buena habilidad para cualquier desarrollador que trabaje con JSP. Y, como ha elegido un estándar como JSP para la presentación, hay muchas posibilidades de que tenga herramientas que lo ayudarán.
Este enfoque tiene muchas ventajas sobre la alternativa de agregar más métodos al modelo subyacente. Esta función se puede escribir una vez y reutilizar en cualquier proyecto. Funciona con una biblioteca de fuente cerrada y de terceros. Se pueden admitir diferentes delimitadores en diferentes contextos, sin contaminar un API modelo con un nuevo método para cada uno.
Lo que es más importante, es compatible con una separación de la vista y los roles de desarrollo del controlador de modelo. Las tareas en estas dos áreas a menudo son realizadas por diferentes personas en diferentes momentos. El mantenimiento de un acoplamiento flexible entre estas capas minimiza la complejidad y los costos de mantenimiento. Cuando incluso un cambio trivial como usar un delimitador diferente en la presentación requiere que un programador modifique una biblioteca, usted tiene un sistema muy costoso y engorroso.
La clase StringUtil
es la misma ya sea que esté expuesta como una función EL o no. El único "extra" necesario es el TLD, que es trivial; una herramienta podría generarlo fácilmente.
Creo que esto es lo que quieres:
<c:forEach var="tab" items="${tabs}">
<c:set var="tabAttrs" value=''${tabAttrs} ${tab.key}="${tab.value}"''/>
</c:forEach>
En este caso, tenía un hashmap con pestaña ID (clave) y URL (valor). La variable tabAttrs no está establecida antes de esto. Por lo tanto, simplemente establece el valor en el valor actual de tabAttrs ('''' para comenzar) más la expresión clave / valor.
Simplemente coloque la cadena junto a la var del servidor, así:
<c:forEach items="${upcomingSchedule}" var="conf">
<div class=''scheduled'' title="${conf.subject}"
id="scheduled${conf.id}">
...
</div>
</c:forEach>
¡¡¡Demasiado tarde!!!
La forma en que se implementan las bibliotecas de etiquetas parece haber cambiado considerablemente desde que esta respuesta se publicó originalmente, así que terminé haciendo algunos cambios drásticos para que las cosas funcionen. Mi resultado final fue:
Archivo de biblioteca de etiquetas:
<?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;
}
}
Para usarlo:
<%@ taglib prefix="su" uri="/WEB-INF/tlds/string_util.tld" %>
[new Date(${row.key}), <su:joinints integers="${row.value}" separator="," />],
Puedes usar la API EL 3.0 Stream. Por ejemplo, si tiene una lista de cadenas,
<div>${stringList.stream().reduce(",", (n,p)->p.concat(n))}</div>
En caso de que tengas una lista de objetos para ex. Person (firstName, lastName) y desea hacer coincidir solo una propiedad de ellos (ex firstName) puede usar map,
<div>${personList.stream().map(p->p.getFirstName()).reduce(",", (n,p)->p.concat(n))}</div>
En tu caso podrías usar algo como eso (eliminar el último '','' también),
<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>
¡Ten cuidado! La API EL 3.0 Stream se finalizó antes de Java 8 Stream API y es diferente a eso. No pueden sunc ambas apis porque romperá la compatibilidad hacia atrás.