2008-11-17 17 views
15

Tengo una Lista de frijoles, cada uno de los cuales tiene una propiedad que a su vez es una Lista de direcciones de correo electrónico.Cadenas de concatenación en JSP EL?

<c:forEach items="${upcomingSchedule}" var="conf"> 
    <div class='scheduled' title="${conf.subject}" id="scheduled<c:out value="${conf.id}"/>"> 
    ... 
    </div> 
</c:forEach> 

Esto hace que uno <div> por frijol en la Lista.

Para la sublista, lo que me gustaría hacer es concatenar cada una de las entradas de la lista para formar una sola Cadena, que se mostrará como parte del atributo de <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.

lo tanto, si ${conf.subject} era "Sujeto", en última instancia, me gustaría que el title del <div> ser "Asunto: [email protected], [email protected], etc.", que contiene todas 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.

+0

Posible duplicado de [Concatenación de cadenas JSP EL] (http://stackoverflow.com/questions/3189642/jsp-el-string-concatenation) –

Respuesta

8

descubierto una manera un poco sucia para 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> 

sólo tiene que utilizar <c:set> en varias ocasiones, haciendo referencia a su propio valor, para anexar/concatenar las cadenas.

+0

Creo que esto es lo mejor que puede hacer sin escribir su propia función (que no es demasiado difícil) en la línea de fn: join – erickson

1

Si su lista secundaria es un ArrayList y hacer esto:

<div class='scheduled' title="${conf.subject}: ${conf.invitees}" id="scheduled${conf.id}"> 

a obtener casi lo que necesita.

La única diferencia es que el título será: "Asunto: [[email protected], [email protected], etc.]".

Tal vez puede ser lo suficientemente bueno para usted.

+0

Eso funciona de alguna manera si la lista subyacente es una ArrayList, pero no me gustaría correr el riesgo de que fuera alguna otra implementación de List sin la misma toString() Implementación. –

16

La forma "limpia" de hacer esto sería usar una función. Como la función JSTL join no funcionará en un Collection, puede escribir la suya sin demasiados problemas y volver a utilizarla en lugar de cortar y pegar un gran trozo de 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.

He aquí un bosquejo:

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> 

Mientras que el TLD es una 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 más importante es que es compatible con una separación de vista y roles de desarrollo de 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.

+0

¿Por qué crees que es mejor escribir una función jstl personalizada que un método más en el bean de respaldo? No es una pregunta provocativa. En un caso como este, escribiría un método para esto en el bean, algo así como getInviteesAsString(). ¿Qué pasa con esto? – alexmeia

+0

Agregué una respuesta a mi respuesta. Te plantearía la misma pregunta: ¿por qué crees que es mejor cambiar el bean de respaldo? – erickson

+0

Pensé que era mejor porque admite una separación de vista y roles de desarrollo de controlador de modelo, y deja la menor lógica posible en la vista. Pero, con el agregado a su respuesta, entiendo que también su método admite esta separación y también tiene otros beneficios. Gracias por agregarme. – alexmeia

0

creo que esto es lo que quiere:

<c:forEach var="tab" items="${tabs}"> 
<c:set var="tabAttrs" value='${tabAttrs} ${tab.key}="${tab.value}"'/> 
</c:forEach> 

En este caso, yo tenía un HashMap con pestaña Identificación (clave) y la 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.

+0

El OP ya respondió su propia pregunta :) Compruebe el mensaje con la gran marca de verificación verde. – BalusC

0

Sólo hay que poner la cadena byside la var desde el servidor, así:

<c:forEach items="${upcomingSchedule}" var="conf"> 
    <div class='scheduled' title="${conf.subject}" 

     id="scheduled${conf.id}"> 

    ... 
    </div> 
</c:forEach> 

demasiado tarde !!!

0

La manera en que se implementan las bibliotecas de etiquetas parece haber cambiado considerablemente desde que se publicó originalmente esta respuesta, así que terminé haciendo algunos cambios drásticos para que las cosas funcionen. Mi resultado final fue:

Tag Archivo Biblioteca:

<?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; 
    } 
} 

utilizarlo:

<%@ taglib prefix="su" uri="/WEB-INF/tlds/string_util.tld" %> 

[new Date(${row.key}), <su:joinints integers="${row.value}" separator="," />], 
0

Puede utilizar la API de Corriente EL 3.0. Por ejemplo, si tiene una lista de cadenas,

<div>${stringList.stream().reduce(",", (n,p)->p.concat(n))}</div> 

En caso de tener una lista de objetos para ej. Persona (nombre, apellido) y desea a concat sólo una propiedad de ellos (por ejemplo primerNombre) se puede usar un mapa,

<div>${personList.stream().map(p->p.getFirstName()).reduce(",", (n,p)->p.concat(n))}</div> 

En su caso, usted podría utilizar algo por el estilo (quitar la última '', 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> 

¡Cuidado! El EL 3.0 Stream API finalizó antes de la API Java 8 Stream y es diferente. No pueden sunc ambas apis porque romperá la compatibilidad hacia atrás.

Cuestiones relacionadas