JSP-Tricks, um das Templating zu vereinfachen?

305

Bei der Arbeit wurde ich beauftragt, eine Reihe von HTMLDateien in ein einfaches JSPProjekt umzuwandeln. Es ist wirklich alles statisch, keine serverseitige Logik zum Programmieren. Ich sollte erwähnen, dass ich in Java völlig neu bin. JSP-Dateien scheinen es einfach zu machen, mit allgemeinen Includes und Variablen zu arbeiten, ähnlich PHP, aber ich würde gerne einen einfachen Weg kennen, um so etwas wie eine Vorlagenvererbung zu erhalten (Django Stil) zu erhalten oder zumindest eine base.jsp-Datei zu haben die Kopf- und Fußzeile, damit ich später Inhalte einfügen kann.

Ben Lings scheint hier in seiner Antwort Hoffnung zu geben: Vererbung von JSP-Vorlagen Kann jemand erklären, wie dies erreicht werden kann?

Da ich nicht viel Zeit habe, denke ich, dass dynamisches Routing ein wenig viel ist, und ich bin froh, dass URLs direkt darauf abgebildet werden .jsp Dateien werden, bin aber offen für Vorschläge.

Vielen Dank.

Bearbeiten: Ich möchte keine externen Bibliotheken verwenden, da dies die Lernkurve für mich und andere, die an dem Projekt arbeiten, verlängern würde. Das Unternehmen, für das ich arbeite, wurde damit beauftragt.

Noch eine Änderung: Ich bin mir nicht sicher, ob JSP tagsdies nützlich sein wird, da mein Inhalt keine Vorlagenvariablen enthält. Was ich brauche, ist eine Möglichkeit, dies zu tun:

base.html:

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

somepage.html

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

mit der Ausgabe ist:

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

Ich denke, das würde mir genug Vielseitigkeit geben, um alles zu tun, was ich brauche. Es könnte mit erreicht werden, includesaber dann würde ich für jeden Wrapper ein Top- und ein Bottom-Include benötigen, was irgendwie chaotisch ist.

Scott
quelle

Antworten:

682

Wie von Skaffman vorgeschlagen , JSP 2.0-Tag-Dateien die Knie der Biene.

Nehmen wir Ihr einfaches Beispiel.

Geben Sie Folgendes ein WEB-INF/tags/wrapper.tag

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

Jetzt auf Ihrer example.jspSeite:

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

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

Das macht genau das, was Sie denken.


Lassen Sie uns das auf etwas allgemeineres erweitern. 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>

So verwenden Sie:

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

Was kauft dir das? Wirklich viel, aber es wird noch besser ...


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>

Um dies zu verwenden: (Angenommen, wir haben eine Benutzervariable in der Anfrage)

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

Es stellt sich jedoch heraus, dass Sie diesen Benutzerdetailblock gerne an anderen Stellen verwenden. Also werden wir es umgestalten. 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/>

Das vorherige Beispiel lautet nun:

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

Das Schöne an JSP-Tag-Dateien ist, dass Sie damit generisches Markup markieren und es dann nach Herzenslust umgestalten können.

JSP Tag Fileshabe so ziemlich Dinge wie Tilesetc. usurpiert , zumindest für mich. Ich finde sie viel einfacher zu benutzen, da die einzige Struktur das ist, was du gibst, nichts vorgefasstes. Außerdem können Sie JSP-Tag-Dateien für andere Zwecke verwenden (z. B. das obige Benutzerdetailfragment).

Hier ist ein Beispiel, das dem von mir erstellten DisplayTag ähnelt, aber dies alles geschieht mit Tag-Dateien (und dem StripesFramework, das sind die s: tags ..). Dies führt zu einer Tabelle mit Zeilen, wechselnden Farben, Seitennavigation usw.:

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

Natürlich funktionieren die Tags mit dem JSTL tags (likec:if usw.). Das einzige, was Sie im Hauptteil eines Tag-Datei-Tags nicht tun können, ist das Hinzufügen von Java-Scriptlet-Code. Dies ist jedoch keine so große Einschränkung, wie Sie vielleicht denken. Wenn ich Scriptlet-Inhalte benötige, füge ich die Logik einfach in ein Tag ein und füge das Tag ein. Einfach.

Tag-Dateien können also so ziemlich alles sein, was Sie möchten. Auf der einfachsten Ebene ist es einfaches Refactoring zum Ausschneiden und Einfügen. Nehmen Sie einen Teil des Layouts, schneiden Sie ihn aus, führen Sie eine einfache Parametrisierung durch und ersetzen Sie ihn durch einen Tag-Aufruf.

Auf einer höheren Ebene können Sie anspruchsvolle Dinge wie dieses Tabellen-Tag tun, das ich hier habe.

Will Hartung
quelle
34
Danke dafür. Es ist das beste Tutorial, das ich für JSP-Tag-Dateien finden konnte, die für mich von JSF großartig waren. Ich wünschte, ich könnte mehr als eine Stimme abgeben.
Digitaljoel
66
+ 40 Millionen. Vielen Dank, dass Sie es 50.000 Mal besser erklärt haben als jedes beschissene Tutorial, das ich gefunden habe. Ich komme aus der Rails-Welt und vermisse ERB. Genau das brauche ich. Du solltest einen Blog schreiben.
cbmeeks
2
Wirklich schönes Tutorial. Können Sie uns den Code für das von Ihnen erstellte Tabellen-Tag mitteilen? Ich habe vor einiger Zeit selbst eine erstellt, aber Ihr Ansatz ist besser.
Thiago Duarte
4
Wenn Sie ein Tag-Datei-Tag erstellen, darf der Inhalt dieses Tags in der JSP-Datei keinen Scriptlet-Code enthalten: <t: mytag> hier kein Scriptlet-Code </ t: mytag>. Aber innerhalb der Tag-Datei, die das Tag selbst implementiert, kann es wie jede JSP den gesamten gewünschten Scriptlet-Code enthalten.
Will Hartung
4
Hinweis - Es scheint, dass die Reihenfolge der Tags wichtig ist. Das Attribut jsp: muss vor jsp: body stehen, sonst wird eine Fehlermeldung angezeigt. Außerdem musste ich ein entsprechendes @ attribute-Tag festlegen, das mit jsp: invoke übereinstimmt, um einen weiteren Fehler zu vermeiden. Verwenden von GlassFish 3.2.2
Ryan
21

Ich habe ganz einfach eine JSP-Vorlagen-Vererbungs-Tag-Bibliothek im Django-Stil erstellt. https://github.com/kwon37xi/jsp-template-inheritance

Ich denke, es ist einfach, Layouts ohne Lernaufwand zu verwalten.

Beispielcode:

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

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

Basierend auf der gleichen Grundidee wie in @Will Hartung Antwort von , hier ist meine magische erweiterbare Template-Engine mit einem Tag. Es enthält sogar Dokumentation und ein Beispiel :-)

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

Verwenden Sie Fliesen . Es hat mein Leben gerettet.

Wenn Sie dies nicht können, gibt es das Include-Tag , das es ähnlich wie PHP macht.

Das Body-Tag macht möglicherweise nicht das, was Sie brauchen, es sei denn, Sie haben einen supereinfachen Inhalt. Das Body-Tag wird verwendet, um den Body eines angegebenen Elements zu definieren. Schauen Sie sich dieses Beispiel an :

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

Sie geben den Elementnamen, alle Attribute an, die das Element möglicherweise hat (in diesem Fall "lang"), und dann den darin enthaltenen Text - den Body. Also wenn

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

Dann wäre die Ausgabe

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

Fügen Sie Abhängigkeiten zur Verwendung hinzu. <% @ tag description = "Benutzerseitenvorlage" 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
quelle
-1

Ich weiß, dass diese Antwort Jahre später kommt und es bereits eine großartige JSP-Antwort von Will Hartung gibt, aber es gibt Facelets, die sogar in den Antworten der verknüpften Frage in der ursprünglichen Frage erwähnt werden.

Facelets SO-Tag-Beschreibung

Facelets ist eine XML-basierte Ansichtstechnologie für das JavaServer Faces-Framework. Facelets wurde speziell für JSF entwickelt und soll eine einfachere und leistungsfähigere Alternative zu JSP-basierten Ansichten sein. Ursprünglich ein separates Projekt, wurde die Technologie als Teil von JSF 2.0 und Java-EE 6 standardisiert und hat JSP veraltet. Fast alle JSF 2.0-Komponentenbibliotheken unterstützen JSP nicht mehr, sondern nur noch Facelets.

Leider war die beste einfache Tutorial-Beschreibung, die ich gefunden habe, auf Wikipedia und keine Tutorial-Site. Tatsächlich entspricht der Abschnitt, in dem Vorlagen beschrieben werden, sogar dem, was in der ursprünglichen Frage gefordert wurde.

Aufgrund der Tatsache, dass Java-EE 6 JSP veraltet hat, würde ich Facelets empfehlen, obwohl es den Anschein hat, dass mehr für wenig bis gar keinen Gewinn gegenüber JSP erforderlich ist.

Fering
quelle
Java EE 6 hat JSP nicht veraltet, sondern nur JSP als Ansichtstechnologie für JSF verwendet.
Ryan
@Ryan Da in diesem Fall beide über die View-Technologie sprachen, was ist falsch daran, gesagt zu haben, dass sie veraltet ist?
Fering
Die Frage hat nichts mit JSF zu tun. Es geht um reine JSP. Ihre Antwort ist die Verwendung von Facelets für JSF.
Ryan