Pregunta

Tengo una Lista de beans, cada uno de los cuales tiene una propiedad que 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 uno <div> por bean en la Lista.

Para la sublista, lo que me gustaría poder hacer es concatenar cada una de las entradas de la lista para formar una sola Cadena, que se mostrará como parte del atributo title 's ${conf.subject} . ¿Por qué? Debido a que estamos utilizando una biblioteca javascript (mootools) para convertir esto <=> en una información sobre herramientas flotante, y la biblioteca convierte el <=> en el texto de la información sobre herramientas.

Entonces, si <=> era " Asunto " ;, finalmente me gustaría que el <=> de <=> sea " Asunto: blah@blah.com , blah2@blah2.com, etc. " ;, que contiene todas las direcciones de correo electrónico de la sublista.

¿Cómo puedo hacer esto usando JSP EL? Estoy tratando de evitar poner bloques de scriptlet en el archivo jsp.

¿Fue útil?

Solución 2

Descubrí una forma un tanto 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 agregar / concatenar las cadenas.

Otros consejos

El " clean " La forma de hacerlo sería utilizar una función. Como la función JSTL join no funcionará en un Collection, puede escribir la suya sin demasiados problemas y reutilizarla por todas partes en lugar de cortar y pegar una gran porción 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. Póngalos juntos en un JAR y colóquelo 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 / 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>

Si bien el TLD es un poco detallado, conocer a uno es una buena habilidad para cualquier desarrollador que trabaje con JSP. Y, dado que ha elegido un estándar como JSP para la presentación, es muy probable que tenga herramientas que lo ayuden.

Este enfoque tiene muchas ventajas sobre la alternativa de agregar más métodos al modelo subyacente. Esta función puede escribirse una vez y reutilizarse en cualquier proyecto. Funciona con una biblioteca de terceros de código cerrado. Se pueden admitir diferentes delimitadores en diferentes contextos, sin contaminar un modelo de API con un nuevo método para cada uno.

Lo más importante, admite una separación de roles de desarrollo de vista y controlador de modelo. Las tareas en estas dos áreas a menudo las realizan diferentes personas en diferentes momentos. Mantener un acoplamiento flojo 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, tiene un sistema muy costoso y engorroso.

La clase StringUtil es la misma si está expuesta como una función EL o no. El único & Quot; extra & Quot; necesario es el TLD, que es trivial; una herramienta podría generarlo fácilmente.

¿Podría 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: [blah@blah.com, blah2@blah2.com, etc.] " ;.

Tal vez pueda ser lo suficientemente bueno para ti.

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 ID de pestaña (clave) y URL (valor). La variable tabAttrs no se establece antes de esto. Por lo tanto, solo establece el valor en el valor actual de tabAttrs ('' para comenzar) más la expresión clave / valor.

Simplemente coloque la cadena al lado de 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 avanzado 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="," />],

Puede 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 tenga una lista de objetos para ej. Persona (nombre, apellido) y desea concatenar solo una propiedad de ellos (ex nombre) puede usar el mapa,

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

En su caso, podría usar algo así (elimine 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>

¡Tenga cuidado! El API EL 3.0 Stream se finalizó antes de Java 8 Stream API y es diferente a eso. No pueden bloquear ambas API porque romperá la compatibilidad con versiones anteriores.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top