Truques JSP para facilitar a modelagem?

305

No trabalho, fui encarregado de transformar um monte de HTMLarquivos em um JSPprojeto simples . É realmente tudo estático, sem lógica do servidor para programar. Devo mencionar que sou completamente novo em Java. Os arquivos JSP parecem facilitar o trabalho com inclusões e variáveis ​​comuns, assim como PHP, mas eu gostaria de saber uma maneira simples de obter algo como herança de modelo ( Djangoestilo) ou pelo menos poder ter um arquivo base.jsp contendo o cabeçalho e o rodapé, para que eu possa inserir o conteúdo posteriormente.

Ben Lings parece oferecer alguma esperança em sua resposta aqui: Herança de modelo JSP Alguém pode explicar como conseguir isso?

Como não tenho muito tempo, acho que o roteamento dinâmico é um pouco exagerado, por isso estou feliz em ter os URLs mapeados diretamente nos .jsparquivos, mas estou aberto a sugestões.

Obrigado.

editar: não quero usar nenhuma biblioteca externa, porque aumentaria a curva de aprendizado para mim e para outras pessoas que trabalham no projeto, e a empresa em que trabalho foi contratada para fazer isso.

Outra edição: não tenho certeza se JSP tagsserá útil porque meu conteúdo realmente não possui nenhuma variável de modelo. O que eu preciso é de uma maneira de poder fazer isso:

base.html:

<html><body>
{ content.body }
</body></html>

somepage.html

<wrapper:base.html>
<h1>Welcome</h1>
</wrapper>

com a saída sendo:

<html><body>
<h1>Welcome</h1>
</body></html>

Eu acho que isso me daria versatilidade suficiente para fazer tudo o que eu preciso. Isso poderia ser alcançado com, includesmas então eu precisaria de um top e um bottom para cada wrapper, o que é meio bagunçado.

Scott
fonte

Respostas:

682

Como skaffman sugeriu , os arquivos de tags JSP 2.0 são o joelho da abelha.

Vamos dar o seu exemplo simples.

Coloque o seguinte em WEB-INF/tags/wrapper.tag

<%@tag description="Simple Wrapper Tag" pageEncoding="UTF-8"%>
<html><body>
  <jsp:doBody/>
</body></html>

Agora na sua example.jsppágina:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="t" tagdir="/WEB-INF/tags" %>

<t:wrapper>
    <h1>Welcome</h1>
</t:wrapper>

Isso faz exatamente o que você pensa que faz.


Então, vamos expandir isso para algo um pouco mais geral. WEB-INF/tags/genericpage.tag

<%@tag description="Overall Page template" pageEncoding="UTF-8"%>
<%@attribute name="header" fragment="true" %>
<%@attribute name="footer" fragment="true" %>
<html>
  <body>
    <div id="pageheader">
      <jsp:invoke fragment="header"/>
    </div>
    <div id="body">
      <jsp:doBody/>
    </div>
    <div id="pagefooter">
      <jsp:invoke fragment="footer"/>
    </div>
  </body>
</html>

Para usar isso:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="t" tagdir="/WEB-INF/tags" %>

<t:genericpage>
    <jsp:attribute name="header">
      <h1>Welcome</h1>
    </jsp:attribute>
    <jsp:attribute name="footer">
      <p id="copyright">Copyright 1927, Future Bits When There Be Bits Inc.</p>
    </jsp:attribute>
    <jsp:body>
        <p>Hi I'm the heart of the message</p>
    </jsp:body>
</t:genericpage>

O que isso te compra? Muito, mas fica ainda melhor ...


WEB-INF/tags/userpage.tag

<%@tag description="User Page template" pageEncoding="UTF-8"%>
<%@taglib prefix="t" tagdir="/WEB-INF/tags" %>
<%@attribute name="userName" required="true"%>

<t:genericpage>
    <jsp:attribute name="header">
      <h1>Welcome ${userName}</h1>
    </jsp:attribute>
    <jsp:attribute name="footer">
      <p id="copyright">Copyright 1927, Future Bits When There Be Bits Inc.</p>
    </jsp:attribute>
    <jsp:body>
        <jsp:doBody/>
    </jsp:body>
</t:genericpage>

Para usar isso: (suponha que tenhamos uma variável de usuário na solicitação)

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="t" tagdir="/WEB-INF/tags" %>

<t:userpage userName="${user.fullName}">
  <p>
    First Name: ${user.firstName} <br/>
    Last Name: ${user.lastName} <br/>
    Phone: ${user.phone}<br/>
  </p>
</t:userpage>

Mas acontece que você gosta de usar esse bloco de detalhes do usuário em outros lugares. Então, vamos refatorar. WEB-INF/tags/userdetail.tag

<%@tag description="User Page template" pageEncoding="UTF-8"%>
<%@tag import="com.example.User" %>
<%@attribute name="user" required="true" type="com.example.User"%>

First Name: ${user.firstName} <br/>
Last Name: ${user.lastName} <br/>
Phone: ${user.phone}<br/>

Agora o exemplo anterior se torna:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="t" tagdir="/WEB-INF/tags" %>

<t:userpage userName="${user.fullName}">
  <p>
    <t:userdetail user="${user}"/>
  </p>
</t:userpage>

A beleza dos arquivos JSP Tag é que ele permite que você basicamente marque uma marcação genérica e depois refatore-a para o conteúdo do seu coração.

JSP Tag Filesusurpou coisas como Tilesetc., pelo menos para mim. Acho-os muito mais fáceis de usar, pois a única estrutura é o que você fornece, nada preconcebido. Além disso, você pode usar arquivos de tag JSP para outras coisas (como o fragmento de detalhes do usuário acima).

Aqui está um exemplo semelhante ao DisplayTag que eu fiz, mas tudo isso é feito com os arquivos de tag (e a Stripesestrutura, que é s: tags ..). Isso resulta em uma tabela de linhas, cores alternadas, navegação na página, etc:

<t:table items="${actionBean.customerList}" var="obj" css_class="display">
  <t:col css_class="checkboxcol">
    <s:checkbox name="customerIds" value="${obj.customerId}"
                onclick="handleCheckboxRangeSelection(this, event);"/>
  </t:col>
  <t:col name="customerId" title="ID"/>
  <t:col name="firstName" title="First Name"/>
  <t:col name="lastName" title="Last Name"/>
  <t:col>
    <s:link href="/Customer.action" event="preEdit">
      Edit
      <s:param name="customer.customerId" value="${obj.customerId}"/>
      <s:param name="page" value="${actionBean.page}"/>
    </s:link>
  </t:col>
</t:table>

Obviamente, as tags funcionam com o JSTL tags(como c:if, etc.). A única coisa que você não pode fazer no corpo de uma tag de arquivo de tag é adicionar código de scriptlet Java, mas isso não é tão uma limitação quanto você imagina. Se eu precisar de material de scriptlet, basta inserir a lógica em uma tag e soltá-la. Fácil.

Portanto, os arquivos de tags podem ser praticamente o que você deseja que eles sejam. No nível mais básico, é simples refatoração de recortar e colar. Pegue um pedaço do layout, corte-o, faça algumas parametrizações simples e substitua-o por uma chamada de tag.

Em um nível superior, você pode fazer coisas sofisticadas, como essa tag de tabela que tenho aqui.

Will Hartung
fonte
34
Obrigado por isso. É o melhor tutorial que pude encontrar nos arquivos de tag JSP, que foram ótimos para mim vindos do JSF. Gostaria de poder dar mais de um voto positivo.
Digitaljoel 01/11/2010
66
+ 40 milhões. Obrigado por explicá-lo 50.000 vezes melhor do que qualquer tutorial de baixa qualidade que eu encontrei. Vindo do mundo Rails e perdendo o ERB, é exatamente isso que eu preciso. Você deve escrever um blog.
cbmeeks
2
Tutorial muito bom. Você poderia compartilhar conosco o código da tag de tabela que você criou? Eu mesmo criei um tempo atrás, mas sua abordagem é melhor.
Thiago Duarte
4
Se você criar uma tag de arquivo de tag, o conteúdo dessa tag no arquivo JSP não poderá ter código de scriptlet: <t: mytag> nenhum código de scriptlet aqui </ t: mytag>. Porém, no arquivo de tag que implementa a própria tag, ele pode ter todo o código do scriptlet desejado, como qualquer JSP.
Will Hartung
4
Nota - parece que a ordem das tags é importante; O atributo jsp: deve vir antes do jsp: body ou você receberá um erro. Também tive que definir uma tag @attribute correspondente para corresponder ao jsp: invoke para evitar outro erro. Usando GlassFish 3.2.2
Ryan
21

Eu facilitei bastante, a biblioteca de tags de herança JSP Template no estilo Django. https://github.com/kwon37xi/jsp-template-inheritance

Eu acho que facilita o gerenciamento de layouts sem curva de aprendizado.

código de exemplo:

base.jsp: layout

<%@page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="http://kwonnam.pe.kr/jsp/template-inheritance" prefix="layout"%>
<!DOCTYPE html>
<html lang="en">
    <head>
        <title>JSP Template Inheritance</title>
    </head>

<h1>Head</h1>
<div>
    <layout:block name="header">
        header
    </layout:block>
</div>

<h1>Contents</h1>
<div>
    <p>
    <layout:block name="contents">
        <h2>Contents will be placed under this h2</h2>
    </layout:block>
    </p>
</div>

<div class="footer">
    <hr />
    <a href="https://github.com/kwon37xi/jsp-template-inheritance">jsp template inheritance example</a>
</div>
</html>

view.jsp: conteúdo

<%@page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="http://kwonnam.pe.kr/jsp/template-inheritance" prefix="layout"%>
<layout:extends name="base.jsp">
    <layout:put name="header" type="REPLACE">
        <h2>This is an example about layout management with JSP Template Inheritance</h2>
    </layout:put>
    <layout:put name="contents">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin porta,
        augue ut ornare sagittis, diam libero facilisis augue, quis accumsan enim velit a mauris.
    </layout:put>
</layout:extends>
KwonNam
fonte
10

Com base na mesma idéia básica da resposta de @Will Hartung , aqui está o meu mecanismo de modelo extensível de uma tag mágica. Inclusive inclui documentação e um exemplo :-)

WEB-INF / tags / block.tag:

<%--
    The block tag implements a basic but useful extensible template system.

    A base template consists of a block tag without a 'template' attribute.
    The template body is specified in a standard jsp:body tag, which can
    contain EL, JSTL tags, nested block tags and other custom tags, but
    cannot contain scriptlets (scriptlets are allowed in the template file,
    but only outside of the body and attribute tags). Templates can be
    full-page templates, or smaller blocks of markup included within a page.

    The template is customizable by referencing named attributes within
    the body (via EL). Attribute values can then be set either as attributes
    of the block tag element itself (convenient for short values), or by
    using nested jsp:attribute elements (better for entire blocks of markup).

    Rendering a template block or extending it in a child template is then
    just a matter of invoking the block tag with the 'template' attribute set
    to the desired template name, and overriding template-specific attributes
    as necessary to customize it.

    Attribute values set when rendering a tag override those set in the template
    definition, which override those set in its parent template definition, etc.
    The attributes that are set in the base template are thus effectively used
    as defaults. Attributes that are not set anywhere are treated as empty.

    Internally, attributes are passed from child to parent via request-scope
    attributes, which are removed when rendering is complete.

    Here's a contrived example:

    ====== WEB-INF/tags/block.tag (the template engine tag)

    <the file you're looking at right now>

    ====== WEB-INF/templates/base.jsp (base template)

    <%@ page trimDirectiveWhitespaces="true" %>
    <%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>
    <t:block>
        <jsp:attribute name="title">Template Page</jsp:attribute>
        <jsp:attribute name="style">
            .footer { font-size: smaller; color: #aaa; }
            .content { margin: 2em; color: #009; }
            ${moreStyle}
        </jsp:attribute>
        <jsp:attribute name="footer">
            <div class="footer">
                Powered by the block tag
            </div>
        </jsp:attribute>
        <jsp:body>
            <html>
                <head>
                    <title>${title}</title>
                    <style>
                        ${style}
                    </style>
                </head>
                <body>
                    <h1>${title}</h1>
                    <div class="content">
                        ${content}
                    </div>
                    ${footer}
                </body>
            </html>
        </jsp:body>
    </t:block>

    ====== WEB-INF/templates/history.jsp (child template)

    <%@ page trimDirectiveWhitespaces="true" %>
    <%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>
    <t:block template="base" title="History Lesson">
        <jsp:attribute name="content" trim="false">
            <p>${shooter} shot first!</p>
        </jsp:attribute>
    </t:block>

    ====== history-1977.jsp (a page using child template)

    <%@ page trimDirectiveWhitespaces="true" %>
    <%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>
    <t:block template="history" shooter="Han" />

    ====== history-1997.jsp (a page using child template)

    <%@ page trimDirectiveWhitespaces="true" %>
    <%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>
    <t:block template="history" title="Revised History Lesson">
        <jsp:attribute name="moreStyle">.revised { font-style: italic; }</jsp:attribute>
        <jsp:attribute name="shooter"><span class="revised">Greedo</span></jsp:attribute>
    </t:block>

--%>

<%@ tag trimDirectiveWhitespaces="true" %>
<%@ tag import="java.util.HashSet, java.util.Map, java.util.Map.Entry" %>
<%@ tag dynamic-attributes="dynattributes" %>
<%@ attribute name="template" %>
<%
    // get template name (adding default .jsp extension if it does not contain
    // any '.', and /WEB-INF/templates/ prefix if it does not start with a '/')
    String template = (String)jspContext.getAttribute("template");
    if (template != null) {
        if (!template.contains("."))
            template += ".jsp";
        if (!template.startsWith("/"))
            template = "/WEB-INF/templates/" + template;
    }
    // copy dynamic attributes into request scope so they can be accessed from included template page
    // (child is processed before parent template, so only set previously undefined attributes)
    Map<String, String> dynattributes = (Map<String, String>)jspContext.getAttribute("dynattributes");
    HashSet<String> addedAttributes = new HashSet<String>();
    for (Map.Entry<String, String> e : dynattributes.entrySet()) {
        if (jspContext.getAttribute(e.getKey(), PageContext.REQUEST_SCOPE) == null) {
            jspContext.setAttribute(e.getKey(), e.getValue(), PageContext.REQUEST_SCOPE);
            addedAttributes.add(e.getKey());
        }
    }
%>

<% if (template == null) { // this is the base template itself, so render it %>
    <jsp:doBody/>
<% } else { // this is a page using the template, so include the template instead %>
    <jsp:include page="<%= template %>" />
<% } %>

<%
    // clean up the added attributes to prevent side effect outside the current tag
    for (String key : addedAttributes) {
        jspContext.removeAttribute(key, PageContext.REQUEST_SCOPE);
    }
%>
amichair
fonte
4

Use peças . Isso salvou minha vida.

Mas se você não puder, existe a tag include , tornando-a semelhante ao php.

A tag body pode realmente não fazer o que você precisa, a menos que você tenha um conteúdo super simples. A tag body é usada para definir o corpo de um elemento especificado. Veja este exemplo :

<jsp:element name="${content.headerName}"   
   xmlns:jsp="http://java.sun.com/JSP/Page">    
   <jsp:attribute name="lang">${content.lang}</jsp:attribute>   
   <jsp:body>${content.body}</jsp:body> 
</jsp:element>

Você especifica o nome do elemento, quaisquer atributos que esse elemento possa ter ("lang" nesse caso) e, em seguida, o texto que o acompanha - o corpo. Então se

  • content.headerName = h1,
  • content.lang = fre
  • content.body = Heading in French

Então a saída seria

<h1 lang="fr">Heading in French</h1>
geowa4
fonte
0

adicione dependências para uso <% @ tag description = "Modelo da página do usuário" pageEncoding = "UTF-8"%>

<dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
        <scope>provided</scope>
    </dependency>
        <dependency>
            <groupId>javax.servlet.jsp.jstl</groupId>
            <artifactId>javax.servlet.jsp.jstl-api</artifactId>
            <version>1.2.1</version>
        </dependency>
        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
        </dependency>
    </dependencies>
Juan Silupú Maza
fonte
-1

Eu sei que essa resposta está chegando anos após o fato e já existe uma ótima resposta do JSP por Will Hartung, mas há Facelets, eles são mencionados nas respostas da pergunta vinculada na pergunta original.

Descrição da tag Facelets SO

Facelets é uma tecnologia de visualização baseada em XML para a estrutura JavaServer Faces. Projetado especificamente para JSF, o Facelets pretende ser uma alternativa mais simples e poderosa às visualizações baseadas em JSP. Inicialmente, um projeto separado, a tecnologia foi padronizada como parte do JSF 2.0 e Java-EE 6 e substituiu o JSP. Quase todas as bibliotecas de componentes direcionadas ao JSF 2.0 não suportam mais o JSP, mas apenas o Facelets.

Infelizmente, a melhor descrição simples do tutorial que encontrei foi na Wikipedia e não em um site de tutoriais. De fato, a seção que descreve os modelos faz o que a pergunta original estava pedindo.

Devido ao fato de o Java-EE 6 ter descontinuado o JSP, eu recomendaria o Facelets, apesar de parecer que pode haver mais necessidade de pouco ou nenhum ganho sobre o JSP.

Fering
fonte
O Java EE 6 não descontinuou o JSP, apenas descontinuou o uso do JSP como a tecnologia de visualização do JSF.
21419 Ryan
@ Ryan Como, nesse caso, os dois estavam falando sobre a tecnologia de exibição, o que há de errado em dizer que a depreciou?
Fering
A questão não tem nada a ver com o JSF. É sobre JSP puro. Sua resposta é usar Facelets, que é para JSF.
Ryan