Pergunta

Eu tenho uma lista de feijão, cada um dos quais tem uma propriedade que é em si uma lista de endereços de e-mail.

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

Isso torna um <div> per feijão na lista.

Para a sub-lista, o que eu gostaria de ser capaz de fazer é para concatenar cada uma das entradas na lista para formar uma única String, a ser exibido como parte do atributo <div> do title. Por quê? Como estamos usando uma biblioteca de javascript (mootools) para transformar este <div> em uma dica de ferramenta flutuante, ea biblioteca transforma o title no texto da dica de ferramenta.

Então, se ${conf.subject} era "Assunto", em última análise, eu gostaria que o title do <div> ser "Assunto: blah@blah.com, blah2@blah2.com, etc.", contendo todos os endereços de email a sub-lista.

Como posso fazer isso usando JSP EL? Estou tentando ficar longe de colocar blocos scriptlet no arquivo JSP.

Foi útil?

Solução 2

descobriu uma maneira um pouco sujo para fazer isso:

<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>

Eu só uso <c:set> repetidamente, fazendo referência a seu próprio valor, para anexar / concatenar as strings.

Outras dicas

A forma "limpa" de fazer isso seria usar uma função. Como a função JSTL join não vai trabalhar em um Collection, você pode escrever seu próprio sem muita dificuldade, e reutilizá-lo em todo o lugar, em vez de cortar e colar um grande pedaço de código de loop.

Você precisa a implementação da função, e um TLD para deixar o seu aplicativo de know web onde encontrá-lo. Coloque estes juntos em um JAR e soltá-lo em seu diretório WEB-INF / lib.

Aqui está um esboço:

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>

Enquanto o TLD é um pouco detalhado, sabendo o caminho de volta é uma boa habilidade para qualquer desenvolvedor que trabalha com JSP. E, desde que você tenha escolhido um padrão como JSP para a apresentação, há uma boa chance que você tem ferramentas que irão ajudá-lo.

Esta abordagem tem muitas vantagens sobre a alternativa de adicionar mais métodos ao modelo subjacente. Esta função pode ser escrito uma vez e reutilizado em qualquer projeto. Ele funciona com um código-fonte fechado, biblioteca de terceiros. Delimitadores diferentes podem ser suportadas em diferentes contextos, sem poluir um modelo API com um novo método para cada um.

O mais importante, suporta uma separação de vista e desenvolvimento modelo de controlador de papéis. Tarefas nestas duas áreas são muitas vezes realizados por pessoas diferentes em momentos diferentes. A manutenção de um acoplamento flexível entre estas camadas minimiza os custos complexidade e de manutenção. Quando até mesmo uma mudança trivial como usar um delimitador diferente na apresentação requer um programador para modificar uma biblioteca, você tem um sistema muito caro e complicado.

A classe StringUtil é o mesmo se o exposto como uma função EL ou não. O único "extra" necessário é o TLD, que é trivial; uma ferramenta poderia facilmente gerar-lo.

Você poderia usar isso? Parece que ele quer uma matriz em vez de uma lista ..

${fn:join(array, ";")}

http: // java .sun.com / products / jsp / JSTL / 1.1 / docs / tlddocs / fn / join.fn.html

Se o seu sublista é um ArrayList e você fazer isso:

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

você obter quase o que você precisa.

A única diferença é que o título será: "Subject: [. Blah@blah.com, blah2@blah2.com, etc]".

Talvez pode ser bom o suficiente para você.

Eu acho que isso é o que você quer:

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

Neste caso, eu tinha um hashmap com ID guia (key) e URL (valor). A variável tabAttrs não está definido antes desta. Então ele só define o valor para o valor atual de tabAttrs ( '' para começar), mais a expressão de chave / valor.

Basta colocar a corda byside do var do servidor, como este:

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

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

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

Tarde demais !!!

A forma como as bibliotecas de tags são implementadas parece se mudaram consideravelmente desde esta resposta foi originalmente postada por isso, acabei fazendo algumas mudanças drásticas para fazer as coisas funcionarem. Meu resultado final foi:

Tag Biblioteca do arquivo:

<?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 usá-lo:

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

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

Você pode usar a EL 3.0 Fluxo API. Por exemplo, se você tem uma lista de strings,

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

No caso de você ter uma lista de objetos para a ex. Pessoa (nome, sobrenome) e deseja concat apenas uma propriedade deles (ex firstName) você pode usar o mapa,

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

No seu caso, você poderia usar algo como isso (remover o último '' também),

<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! O EL 3.0 fluxo API foi finalizado antes do Java 8 fluxo API e é diferente do que isso. Eles não podem sunc ambas as APIs, porque ele vai quebrar a compatibilidade com versões anteriores.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top