Sztuczki JSP ułatwiające tworzenie szablonów?

305

W pracy miałem za zadanie przekształcić kilka HTMLplików w prosty JSPprojekt. To naprawdę wszystko jest statyczne, bez programowania po stronie serwera. Powinienem wspomnieć, że jestem zupełnie nowy w Javie. Wydaje się, że pliki JSP ułatwiają pracę ze zwykłymi włącznikami i zmiennymi, PHPale chciałbym znać prosty sposób na uzyskanie dziedziczenia szablonów ( Djangostyl) lub przynajmniej mieć plik base.jsp zawierający nagłówek i stopkę, dzięki czemu mogę później wstawić treść.

Wydaje się, że Ben Lings daje nadzieję w odpowiedzi tutaj: Dziedziczenie szablonów JSP Czy ktoś może wyjaśnić, jak to osiągnąć?

Biorąc pod uwagę, że nie mam dużo czasu, myślę, że dynamiczne routing to trochę, więc cieszę się, że adresy URL są mapowane bezpośrednio na .jsppliki, ale jestem otwarty na sugestie.

Dzięki.

edytuj: Nie chcę używać żadnych zewnętrznych bibliotek, ponieważ zwiększyłoby to krzywą uczenia się dla mnie i innych osób pracujących nad projektem, a firma, dla której pracuję, została do tego zobowiązana.

Kolejna edycja: nie jestem pewien, czy JSP tagsbędzie przydatny, ponieważ moja treść tak naprawdę nie ma żadnych zmiennych szablonu. Potrzebuję sposobu, aby to zrobić:

base.html:

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

somepage.html

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

przy czym wynikiem jest:

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

Myślę, że to dałoby mi wystarczającą wszechstronność, aby zrobić wszystko, czego potrzebuję. Można to osiągnąć za pomocą, includesale wtedy potrzebowałbym góry i dołu dla każdego opakowania, co jest trochę bałagan.

Scott
źródło

Odpowiedzi:

682

Jak sugerował skaffman , pliki znaczników JSP 2.0 to kolana pszczół.

Weźmy twój prosty przykład.

Wprowadź następujące dane WEB-INF/tags/wrapper.tag

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

Teraz na twojej example.jspstronie:

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

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

To robi dokładnie to, co myślisz.


Rozbudujmy to do czegoś bardziej ogólnego. 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>

Aby użyć tego:

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

Co to kupuje? Naprawdę dużo, ale robi się jeszcze lepiej ...


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>

Aby użyć tego: (załóżmy, że w żądaniu mamy zmienną użytkownika)

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

Ale okazuje się, że lubisz używać tego bloku danych użytkownika w innych miejscach. A więc zmienimy to. 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/>

Teraz poprzednim przykładem jest:

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

Piękno plików tagów JSP polega na tym, że pozwala ono zasadniczo oznaczać ogólne znaczniki, a następnie refaktoryzować je do zawartości twojego serca.

JSP Tag Filesprawie uzurpowałem sobie takie rzeczy Tiles, przynajmniej dla mnie. Uważam, że są o wiele łatwiejsze w użyciu, ponieważ jedyną strukturą jest to, co dajesz, nic nie z góry założonego. Ponadto możesz używać plików znaczników JSP do innych celów (takich jak fragment szczegółów użytkownika powyżej).

Oto przykład podobny do DisplayTag, który zrobiłem, ale wszystko to odbywa się za pomocą Tag Files (i Stripesframeworka, to jest s: tags ...). Powoduje to powstanie tabeli wierszy, naprzemiennych kolorów, nawigacji strony itp .:

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

Oczywiście tagi działają z JSTL tags(podobnymi c:ifitp.). Jedyną rzeczą, której nie można zrobić w treści znacznika pliku znacznika, jest dodanie kodu skryptletu Java, ale nie jest to tak duże ograniczenie, jak mogłoby się wydawać. Jeśli potrzebuję skryptu, po prostu umieszczam logikę w tagu i upuszczam tag. Łatwo.

Tak więc pliki znaczników mogą być praktycznie takie, jakie chcesz. Na najbardziej podstawowym poziomie jest to proste refaktoryzowanie wycinania i wklejania. Chwyć kawałek układu, wytnij go, dokonaj prostej parametryzacji i zastąp go wywołaniem znacznika.

Na wyższym poziomie możesz robić wyrafinowane rzeczy, takie jak ten tag tabeli, który tu mam.

Will Hartung
źródło
34
Dzięki za to. To najlepszy samouczek, jaki mogłem znaleźć na plikach znaczników JSP, które były dla mnie świetne, pochodzące z JSF. Chciałbym móc oddać więcej niż jeden głos.
digitaljoel
66
+ 40 milionów. Dziękujemy za wyjaśnienie tego 50 000 razy lepiej niż jakikolwiek gówniany samouczek, który znalazłem. Pochodzę ze świata Rails i brakuje ERB, właśnie tego potrzebuję. Powinieneś napisać blog.
cbmeeks,
2
Naprawdę fajny samouczek. Czy możesz podzielić się z nami kodem utworzonego tagu tabeli? Stworzyłem już jakiś czas temu, ale twoje podejście jest lepsze.
Thiago Duarte
4
Jeśli utworzysz znacznik pliku znacznika, zawartość tego znacznika w pliku JSP nie może mieć kodu skryptletu: <t: mytag> nie ma tutaj kodu skryptletu </ t: mytag>. Ale w pliku znacznika implementującym sam znacznik, który może mieć cały kod skryptu, jaki chcesz, jak każdy JSP.
Will Hartung,
4
Uwaga - wydaje się, że kolejność znaczników jest ważna; jsp: atrybut musi pojawić się przed jsp: body, inaczej pojawi się błąd. Musiałem także ustawić odpowiedni tag @attribute, aby pasował do jsp: invoke, aby uniknąć kolejnego błędu. Korzystanie z GlassFish 3.2.2
Ryan
21

Uczyniłem bibliotekę znaczników dziedziczenia szablonów JSP w stylu Django. https://github.com/kwon37xi/jsp-template-inheritance

Myślę, że ułatwia zarządzanie układami bez uczenia się krzywej.

przykładowy kod:

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: zawartość

<%@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
źródło
10

Opierając się na tym samym podstawowym pomyśle, co w odpowiedzi @Will Hartung , oto mój magiczny silnik szablonów z możliwością rozszerzenia o jeden tag. Zawiera nawet dokumentację i przykład :-)

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
źródło
4

Użyj kafelków . Uratowało mi to życie.

Ale jeśli nie możesz, istnieje tag włączający , dzięki czemu jest podobny do php.

Tag treści może nie robić tego, czego potrzebujesz, chyba że masz super prostą treść. Znacznik body służy do definiowania treści określonego elementu. Spójrz na ten przykład :

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

Podajesz nazwę elementu, wszelkie atrybuty, które może mieć element (w tym przypadku „lang”), a następnie tekst, który się w nim znajduje - treść. Więc jeśli

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

Wtedy wynik byłby

<h1 lang="fr">Heading in French</h1>
geowa4
źródło
0

dodaj zależności do użycia <% @ tag description = strona „Szablon strony użytkownika” 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
źródło
-1

Wiem, że ta odpowiedź nadejdzie wiele lat po fakcie i jest już świetna odpowiedź JSP Willa Hartunga, ale są Facelety, są nawet wspomniane w odpowiedziach na powiązane pytanie w pierwotnym pytaniu.

Opis znacznika SO Facelets

Facelets to oparta na XML technologia wyświetlania dla środowiska JavaServer Faces. Zaprojektowany specjalnie dla JSF, Facelets ma być prostszą i potężniejszą alternatywą dla widoków opartych na JSP. Początkowo oddzielny projekt, technologia została ustandaryzowana jako część JSF 2.0 i Java-EE 6 i przestała działać JSP. Prawie wszystkie biblioteki komponentów docelowych JSF 2.0 nie obsługują już JSP, a jedynie Facelets.

Niestety najlepszy opis samouczka, jaki znalazłem, znajduje się w Wikipedii, a nie w witrynie z samouczkami. W rzeczywistości sekcja opisująca szablony jest zgodna z pierwotnym pytaniem.

Z uwagi na fakt, że Java-EE 6 przestało działać JSP, zaleciłbym skorzystanie z Facelets, mimo że wygląda na to, że może być więcej wymaganych za niewielki lub żaden zysk w stosunku do JSP.

Fering
źródło
Java EE 6 nie ma przestarzałej strony JSP, tylko przestarzała przy użyciu JSP jako technologii widoku dla JSF.
Ryan
@Ryan Ponieważ w tym przypadku obaj rozmawiali o technologii wyświetlania, co jest złego w tym, że powiedział, że jest przestarzała?
Fering
Pytanie nie ma nic wspólnego z JSF. Chodzi o czysty JSP. Twoja odpowiedź to użycie Faceletów, które dotyczą JSF.
Ryan