¿Trucos de JSP para facilitar la creación de plantillas?

305

En el trabajo, me encargaron convertir un montón de HTMLarchivos en un JSPproyecto simple . Realmente todo es estático, no hay lógica del lado del servidor para programar. Debo mencionar que soy completamente nuevo en Java. Los archivos JSP parecen facilitar el trabajo con variables y inclusiones comunes, por ejemplo PHP, pero me gustaría conocer una manera simple de obtener algo como herencia de plantillas ( Djangoestilo) o al menos poder tener un archivo base.jsp que contenga el encabezado y el pie de página, para poder insertar contenido más tarde.

Ben Lings parece ofrecer alguna esperanza en su respuesta aquí: herencia de plantillas JSP ¿Alguien puede explicar cómo lograr esto?

Dado que no tengo mucho tiempo, creo que el enrutamiento dinámico es demasiado, así que estoy feliz de que las URL se asignen directamente a los .jsparchivos, pero estoy abierto a sugerencias.

Gracias.

editar: no quiero usar ninguna biblioteca externa, porque aumentaría la curva de aprendizaje para mí y para otras personas que trabajan en el proyecto, y la compañía para la que trabajo ha sido contratada para hacerlo.

Otra edición: no estoy seguro de si JSP tagsserá útil porque mi contenido realmente no tiene ninguna variable de plantilla. Lo que necesito es una forma de poder hacer esto:

base.html:

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

somepage.html

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

siendo la salida:

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

Creo que esto me daría suficiente versatilidad para hacer todo lo que necesito. Se podría lograr con, includespero luego necesitaría una inclusión superior e inferior para cada envoltorio, lo cual es un poco desordenado.

Scott
fuente

Respuestas:

682

Como sugirió skaffman , los archivos de etiquetas JSP 2.0 son las rodillas de la abeja.

Tomemos su sencillo ejemplo.

Ponga lo siguiente en WEB-INF/tags/wrapper.tag

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

Ahora en tu example.jsppágina:

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

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

Eso hace exactamente lo que crees que hace.


Entonces, ampliemos eso a algo un poco más general. 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 esto:

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

¿Qué te compra eso? Mucho realmente, pero se pone aún mejor ...


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 esto: (suponga que tenemos una variable de usuario en la solicitud)

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

Pero resulta que le gusta usar ese bloque de detalles de usuario en otros lugares. Entonces, lo refactorizaremos. 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/>

Ahora el ejemplo anterior se convierte en:

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

La belleza de los archivos JSP Tag es que básicamente le permite etiquetar el marcado genérico y luego refactorizarlo al contenido de su corazón.

JSP Tag Fileshan usurpado cosas como Tilesetc., al menos para mí. Los encuentro mucho más fáciles de usar ya que la única estructura es lo que le das, nada preconcebido. Además, puede usar archivos de etiquetas JSP para otras cosas (como el fragmento de detalles del usuario anterior).

Aquí hay un ejemplo que es similar a DisplayTag que he hecho, pero todo esto se hace con Tag Files (y el Stripesmarco, ese es el s: tags ...). Esto da como resultado una tabla de filas, colores alternos, navegación de 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>

Por supuesto, las etiquetas funcionan con JSTL tags(como c:if, etc.). Lo único que no puede hacer dentro del cuerpo de una etiqueta de archivo de etiqueta es agregar código Java Scriptlet, pero esto no es una limitación tan grande como podría pensar. Si necesito cosas de scriptlet, simplemente pongo la lógica en una etiqueta y la dejo caer. Fácil.

Por lo tanto, los archivos de etiquetas pueden ser prácticamente lo que quieras que sean. En el nivel más básico, es una simple refactorización de cortar y pegar. Tome un trozo de diseño, recórtelo, realice una parametrización simple y reemplácelo con una invocación de etiqueta.

En un nivel superior, puede hacer cosas sofisticadas como esta etiqueta de tabla que tengo aquí.

Will Hartung
fuente
34
Gracias por esto. Es el mejor tutorial que pude encontrar en los archivos de etiquetas JSP, que fueron geniales para mí viniendo de JSF. Ojalá pudiera dar más de un voto.
digitaljoel
66
+ 40 millones. Gracias por explicarlo 50,000 veces mejor que cualquier tutorial horrible que he encontrado. Viniendo del mundo de Rails y faltando ERB, esto es exactamente lo que necesito. Deberías escribir un blog.
cbmeeks
2
Muy buen tutorial. ¿Podría compartir con nosotros el código de la etiqueta de tabla que ha creado? Creé uno yo mismo hace un tiempo, pero su enfoque es mejor.
Thiago Duarte
44
Si crea una etiqueta de archivo de etiqueta, el contenido de esa etiqueta en el archivo JSP no puede tener código scriptlet: <t: mytag> no hay código scriptlet aquí </ t: mytag>. Pero dentro del archivo de etiqueta que implementa la etiqueta en sí, eso puede tener todo el código de scriptlet que desee, como cualquier JSP.
Will Hartung
44
Nota: parece que el orden de las etiquetas es importante; jsp: el atributo debe aparecer antes de jsp: body o obtendrá un error. También tuve que establecer una etiqueta @attribute correspondiente para que coincida con jsp: invoke para evitar otro error. Usando GlassFish 3.2.2
Ryan
21

Hice una biblioteca de etiquetas de herencia de plantilla JSP de estilo Django bastante fácil. https://github.com/kwon37xi/jsp-template-inheritance

Creo que es fácil administrar diseños sin curva de aprendizaje.

código de ejemplo:

base.jsp: diseño

<%@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: contenido

<%@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
fuente
10

Basado en la misma idea básica que en @Will Hartung , aquí está mi motor mágico de plantilla extensible de una etiqueta. Incluso incluye documentación y un ejemplo :-)

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

Usa azulejos . Me salvó la vida.

Pero si no puede, existe la etiqueta include , que lo hace similar a php.

Es posible que la etiqueta del cuerpo no haga lo que necesita, a menos que tenga contenido súper simple. La etiqueta body se usa para definir el cuerpo de un elemento especificado. Echa un vistazo a este ejemplo :

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

Usted especifica el nombre del elemento, cualquier atributo que ese elemento pueda tener ("lang" en este caso) y luego el texto que lo contiene: el cuerpo. Así que si

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

Entonces la salida sería

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

agregar dependencias para usar <% @ tag description = "Plantilla de página de usuario" 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
fuente
-1

Sé que esta respuesta llegará años después del hecho y ya hay una gran respuesta JSP de Will Hartung, pero hay Facelets, incluso se mencionan en las respuestas de la pregunta vinculada en la pregunta original.

Descripción de la etiqueta Facelets SO

Facelets es una tecnología de vista basada en XML para el marco JavaServer Faces. Diseñado específicamente para JSF, Facelets pretende ser una alternativa más simple y poderosa a las vistas basadas en JSP. Inicialmente un proyecto separado, la tecnología se estandarizó como parte de JSF 2.0 y Java-EE 6 y ha dejado de ser JSP. Casi todas las bibliotecas de componentes específicos de JSF 2.0 ya no son compatibles con JSP, sino solo Facelets.

Lamentablemente, la mejor descripción de tutorial simple que encontré estaba en Wikipedia y no en un sitio de tutorial. De hecho, la sección que describe las plantillas incluso hace lo mismo que la pregunta original.

Debido al hecho de que Java-EE 6 ha desaprobado JSP, recomendaría usar Facelets a pesar del hecho de que parece que podría ser más necesario para obtener poca o ninguna ganancia sobre JSP.

Fering
fuente
Java EE 6 no ha desaprobado JSP, solo desaprobó el uso de JSP como tecnología de visualización para JSF.
Ryan
@Ryan Dado que en este caso ambos estaban hablando de la tecnología de visualización, ¿qué hay de malo en decir que la desaprobó?
Fering
La pregunta no tiene nada que ver con JSF. Se trata de puro JSP. Su respuesta es usar Facelets, que es para JSF.
Ryan