2009-08-18 9 views
276

Al lavoro mi è stato assegnato il compito di trasformare un gruppo di file HTML in un semplice progetto JSP. È davvero tutto statico, nessuna logica serveride da programmare. Dovrei menzionare che sono completamente nuovo in Java. I file JSP sembrano semplificare il lavoro con le inclusioni e le variabili comuni, molto simile a PHP, ma mi piacerebbe sapere un modo semplice per ottenere qualcosa come l'ereditarietà dei modelli (stile Django) o almeno avere una base.jsp file contenente l'intestazione e il piè di pagina, così posso inserire il contenuto più tardi.I trucchi JSP per semplificare i templating?

Ben Lings sembra offrire qualche speranza nella sua risposta qui: JSP template inheritance Qualcuno può spiegare come raggiungere questo obiettivo?

Dato che non ho molto tempo, penso che il routing dinamico è un po 'troppo, quindi sono contento di avere solo gli URL mappati direttamente sui file .jsp, ma sono aperto a suggerimenti.

Grazie.

edit: Non voglio usare librerie esterne, perché aumenterebbe la curva di apprendimento per me e gli altri che lavorano al progetto, e l'azienda per cui lavoro è stato contratto per fare questo.

Un'altra modifica: Non sono sicuro se JSP tags sarà utile perché il mio contenuto non ha realmente alcuna variabile di modello. Che cosa ho bisogno è un modo per essere in grado di fare questo:

base.html:

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

somepage.html

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

con l'essere uscita:

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

penso che questo avrebbe dato mi abbastanza versatilità per fare tutto ciò di cui ho bisogno. Potrebbe essere ottenuto con includes, ma poi avrei bisogno di una parte superiore e una inferiore per ogni wrapper, che è un po 'disordinato.

risposta

628

As skaffman suggested, File tag JSP 2.0 sono le ginocchia delle api.

Facciamo il tuo semplice esempio.

Inserire il seguente in WEB-INF/tags/wrapper.tag

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

Ora nella tua pagina example.jsp:

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

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

che fa esattamente quello che pensi lo fa.


Quindi, passiamo ad approfondire ciò a qualcosa di un po '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 utilizzare 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 che si acquista? Un sacco davvero, ma è ancora meglio ...


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 utilizzare questa: (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 fa girare il blocco dei dettagli dell'utente in altri luoghi. Quindi, lo rifatteremo. 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 di file di tag JSP è che ti permette di fondamentalmente tag markup generico e quindi refactoring al contenuto del vostro cuore.

JSP Tag Files hanno praticamente usurpato cose come ecc., Almeno per me. Li trovo molto più facili da usare in quanto l'unica struttura è ciò che gli dai, niente preconcetto. Inoltre è possibile utilizzare i file di 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 framework Stripes, ovvero s: tags ..). Ciò si traduce in una tabella di righe, colori alternati, pagina di navigazione, 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> 

Naturalmente i tag lavorano con il JSTL tags (come c:if, etc.). L'unica cosa che non puoi fare all'interno del corpo di un tag del tag è aggiungere il codice scriptlet Java, ma non è una limitazione come potresti pensare. Se ho bisogno di materiale scriptlet, inserisco la logica in un tag e rilasciamo il tag. Facile.

Quindi, i file di tag possono essere praticamente ciò che volete che siano. Al livello più elementare, è semplice refactoring di taglia e incolla. Prendi una porzione di layout, tagliala, esegui una semplice parametrizzazione e sostituiscila con una chiamata di tag.

Ad un livello superiore, puoi fare cose sofisticate come questo tag tabella che ho qui.

+32

Grazie per questo.È il miglior tutorial che ho potuto trovare sui file di tag JSP, che per me sono stati eccezionali grazie a JSF. Vorrei poter dare più di un voto. – digitaljoel

+59

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

+2

Davvero un bel tutorial. Potresti condividere con noi il codice per il tag del tavolo che hai creato? Ne ho creato uno anch'io qualche tempo fa ma il tuo approccio è migliore. –

3

Utilizzare tiles. Mi ha salvato la vita.

Ma se non ci riesci, c'è lo include tag, che lo rende simile a php.

Il tag body potrebbe non eseguire realmente ciò che è necessario, a meno che non si disponga di un contenuto super semplice. Il tag body viene utilizzato per definire il corpo di un elemento specificato. Date un'occhiata a this example:

<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 elemento potrebbe avere ("lang" in questo caso), e quindi il testo che va in esso - il corpo.Quindi, se

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

L'uscita sarebbe

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

È necessario essere consapevoli che l'uso di JSP con un sacco di <% ...%> dappertutto, ha generalmente dimostrato di creare applicazioni che sono ha per mantenere (di una dimensione non banale).

Quindi, dovresti già ora prepararti per dover imparare un ulteriore livello, sia per questo che per il prossimo progetto. Personalmente ho scelto JSF per un progetto che permetteva pagine XML puramente XML che invocavano tagli, che era abbastanza bello, ma aveva una curva di apprendimento molto ripida quindi non lo consiglierei a meno che non lo abbiate attentamente considerato prima :)

quale tecnologia scegli, prendine una che ti consenta di separare la presentazione dal codice reale. Lo apprezzerai un giorno.

+6

C'è un divario davvero enorme tra <% %> include, e JSF, e questo divario è pieno di decine di soluzioni incrementalmente più complesse. – skaffman

+0

Sì. È una curva di apprendimento ripida :( –

+5

Ho scoperto che l'utilizzo di uno qualsiasi dei "miglioramenti" JSP su <%...%> comporta applicazioni ancora più difficili da mantenere. – Magnus

19

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

penso che rendono facile da gestire i layout senza curva di apprendimento.

codice

esempio:

base.jsp: il 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: contenuti

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

Sulla base della stessa idea di base come in risposta @Will Hartung s', ecco il mio motore di template estensibile magic one-tag. Esso comprende 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); 
    } 
%>