Trucchi JSP per semplificare il templating?


305

Al lavoro mi è stato affidato il compito di trasformare un mucchio di HTMLfile in un semplice JSPprogetto. È davvero tutto statico, nessuna logica lato server da programmare. Dovrei menzionare che sono completamente nuovo in Java. I file JSP sembrano semplificare il lavoro con inclusioni e variabili comuni, molto simili PHP, ma mi piacerebbe sapere un modo semplice per ottenere qualcosa come l'ereditarietà dei modelli ( Djangostile) o almeno essere in grado di avere un file base.jsp contenente l'intestazione e il piè di pagina, quindi posso inserire il contenuto in un secondo momento.

Ben Lings sembra offrire qualche speranza nella sua risposta qui: eredità del modello JSP Qualcuno può spiegare come raggiungere questo obiettivo?

Dato che non ho molto tempo, penso che il routing dinamico sia un po 'troppo, quindi sono felice di avere la mappatura degli URL direttamente sui .jspfile, ma sono aperto al suggerimento.

Grazie.

modifica: non voglio usare nessuna libreria esterna, perché aumenterebbe la curva di apprendimento per me e gli altri che lavorano al progetto, e la società per cui lavoro è stata contratta per farlo.

Un'altra modifica: non sono sicuro che JSP tagssarà utile perché il mio contenuto non ha davvero alcuna variabile di modello. Quello di cui ho bisogno è un modo per essere in grado di farlo:

base.html:

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

somepage.html

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

con l'output che è:

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

Penso che questo mi darebbe abbastanza versatilità per fare tutto ciò di cui ho bisogno. Potrebbe essere realizzato con, includesma poi avrei bisogno di una parte superiore e una parte inferiore per ogni wrapper, che è un po 'disordinato.

Risposte:


682

Come ha suggerito skaffman , i file di tag JSP 2.0 sono le ginocchia dell'ape.

Facciamo il tuo semplice esempio.

Inserisci quanto segue WEB-INF/tags/wrapper.tag

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

Ora nella tua example.jsppagina:

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

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

Questo fa esattamente quello che pensi che faccia.


Quindi, espandiamoci su qualcosa di più generale. 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>

Per usare questo:

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

Cosa ti compra? Molto davvero, ma migliora ancora ...


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>

Per usare questo: (supponiamo di avere una variabile utente nella richiesta)

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

Ma ti piace usare quel blocco dettagli utente in altri luoghi. Quindi, lo refatteremo. 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/>

Ora l'esempio precedente diventa:

<%@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 bellezza dei file tag JSP è che ti consente sostanzialmente di taggare il markup generico e quindi di rifattorizzarlo in base al contenuto del tuo cuore.

JSP Tag Filesho praticamente usurpato cose come Tilesecc., almeno per me. Li trovo molto più facili da usare in quanto l'unica struttura è ciò che gli dai, niente di preconcetto. Inoltre puoi usare i file tag JSP per altre cose (come il frammento di dettaglio dell'utente sopra).

Ecco un esempio simile a DisplayTag che ho fatto, ma questo è tutto fatto con Tag Files (e il Stripesframework, questo è il s: tags ..). Ciò si traduce in una tabella di righe, alternando colori, navigazione della pagina, ecc:

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

Ovviamente i tag funzionano con JSTL tags(like c:if, ecc.). L'unica cosa che non puoi fare all'interno del corpo di un tag file tag è aggiungere codice scriptlet Java, ma questo non è un limite tanto quanto potresti pensare. Se ho bisogno di materiale scriptlet, inserisco semplicemente la logica in un tag e inserisco il tag. Facile.

Quindi, i file tag possono essere praticamente qualsiasi cosa tu voglia che siano. Al livello più elementare, si tratta di un semplice refactoring. Prendi un pezzo di layout, ritaglialo, esegui una semplice parametrizzazione e sostituiscilo con un richiamo di tag.

A un livello superiore, puoi fare cose sofisticate come questo tag da tavolo che ho qui.


34
Grazie per questo. È il miglior tutorial che ho trovato sui file tag JSP, che sono stati fantastici per me provenienti da JSF. Vorrei poter dare più di un voto positivo.
digitaljoel

66
+ 40 milioni di EUR. Grazie per averlo spiegato 50.000 volte meglio di qualsiasi pessimo tutorial che ho trovato. Proveniente dal mondo Rails e ERB mancante, questo è esattamente ciò di cui ho bisogno. Dovresti scrivere un blog.
cbmeeks,

2
Tutorial davvero carino. Potresti condividere con noi il codice per il tag table che hai creato? Ne ho creato uno anch'io qualche tempo fa ma il tuo approccio è migliore.
Thiago Duarte,

4
Se si crea un tag file tag, il contenuto di quel tag nel file JSP non può avere il codice scriptlet: <t: mytag> nessun codice scriptlet qui </ t: mytag>. Ma all'interno del file tag che implementa il tag stesso, può contenere tutto il codice scriptlet desiderato, come qualsiasi JSP.
Will Hartung,

4
Nota: sembra che l'ordine dei tag sia importante; L'attributo jsp: deve venire prima di jsp: body o otterrai un errore. Inoltre ho dovuto impostare un corrispondente tag @attribute in modo che corrispondesse a jsp: invoke per evitare un altro errore. Utilizzando GlassFish 3.2.2
Ryan

21

Ho reso abbastanza semplice la libreria di tag di ereditarietà del modello JSP in stile Django. https://github.com/kwon37xi/jsp-template-inheritance

Penso che sia facile gestire i layout senza curva di apprendimento.

codice di esempio:

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

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

10

Basato sulla stessa idea di base della risposta di @Will Hartung , ecco il mio magico motore di template estensibile a un tag. Include anche la documentazione e un esempio :-)

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);
    }
%>

4

Usa tessere . Mi ha salvato la vita.

Ma se non puoi, c'è il tag include , che lo rende simile a PHP.

Il tag body potrebbe in realtà non fare ciò di cui hai bisogno, a meno che tu non abbia contenuti super semplici. Il tag body viene utilizzato per definire il corpo di un elemento specificato. Dai un'occhiata a questo esempio :

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

Si specifica il nome dell'elemento, tutti gli attributi che l'elemento potrebbe avere ("lang" in questo caso) e quindi il testo che lo contiene: il corpo. Quindi se

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

Quindi l'output sarebbe

<h1 lang="fr">Heading in French</h1>


0

aggiungi dependecys per l'uso <% @ tag description = "Modello pagina utente" 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>

-1

So che questa risposta sta arrivando anni dopo il fatto e c'è già una grande risposta JSP di Will Hartung, ma ci sono Facelets, sono persino menzionati nelle risposte dalla domanda collegata nella domanda originale.

Descrizione tag SO facelets

Facelets è una tecnologia di visualizzazione basata su XML per il framework JavaServer Faces. Progettato appositamente per JSF, Facelets è pensato per essere un'alternativa più semplice e potente alle viste basate su JSP. Inizialmente un progetto separato, la tecnologia è stata standardizzata come parte di JSF 2.0 e Java-EE 6 e ha deprecato JSP. Quasi tutte le librerie dei componenti di destinazione JSF 2.0 non supportano più JSP, ma solo Facelets.

Purtroppo la migliore descrizione del tutorial che ho trovato era su Wikipedia e non su un sito di tutorial. In effetti la sezione che descrive i modelli fa anche sulla falsariga di ciò che la domanda originale stava chiedendo.

A causa del fatto che Java-EE 6 ha deprecato JSP, consiglierei di utilizzare Facelets nonostante il fatto che sembra che ci potrebbe essere più richiesto per un guadagno minimo o nullo su JSP.


Java EE 6 non ha deprecato JSP, ma solo deprecato usando JSP come tecnologia di visualizzazione per JSF.
Ryan

@Ryan Dato che in questo caso entrambi parlavano della tecnologia di visualizzazione, cosa c'è di sbagliato nell'averlo dichiarato deprecato?
Diteggiatura

La domanda non ha nulla a che fare con JSF. Si tratta di puro JSP. La tua risposta è usare Facelets, che è per JSF.
Ryan,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.