Прийоми JSP, щоб спростити шаблонування?


305

На роботі мені було доручено перетворити купу HTMLфайлів у простий JSPпроект. Це дійсно все статично, ніякої логіки на сервері для програмування. Слід зазначити, що я абсолютно новачок у Java. Файли JSP, схоже, полегшують роботу із загальними включеннями та змінними, як PHP, але я хотів би знати простий спосіб отримати щось на зразок спадкування шаблону ( Djangoстиль) або принаймні мати можливість мати файл base.jsp, що містить заголовок і колонтитул, тому я можу вставити вміст пізніше.

Бен Лінгс, схоже, пропонує надію на свою відповідь тут: успадкування шаблону JSP Чи може хтось пояснити, як цього досягти?

Зважаючи на те, що у мене не так багато часу, я думаю, що динамічна маршрутизація - це небагато, тому я радий просто мати URL-адреси безпосередньо на .jspфайли, але я відкритий для пропозицій.

Дякую.

редагувати: я не хочу використовувати жодних зовнішніх бібліотек, тому що це збільшить криву навчання для себе та інших, хто працює над проектом, і компанія, в якій я працюю, була домовлена ​​про це.

Ще одна редакція: я не впевнений, чи JSP tagsстане в нагоді, оскільки мій вміст насправді не має змінних шаблонів. Що мені потрібно - це спосіб зробити це:

base.html:

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

somepage.html

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

при цьому вихід:

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

Я думаю, що це дасть мені достатню універсальність, щоб зробити все, що мені потрібно. Це можна досягти за допомогою, includesале тоді мені знадобиться верх і низ включати кожну обгортку, яка є безладною.

Відповіді:


682

Як запропонував скафман , файли тегів JSP 2.0 - це коліна бджіл.

Візьмемо ваш простий приклад.

Помістіть наступне WEB-INF/tags/wrapper.tag

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

Зараз на вашій example.jspсторінці:

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

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

Це робить саме те, що ви думаєте, що це робить.


Отже, давайте розширимо це на щось трохи більш загальне. 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>

Щоб скористатися цим:

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

Що це купує у вас? Дійсно, але стає ще краще ...


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>

Щоб скористатися цим: (припустимо, у запиті є змінна користувача)

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

Але виявляється, ви хочете використовувати цей блок деталей користувача в інших місцях. Отже, ми його рефакторируем. 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/>

Тепер попередній приклад стає:

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

Краса файлів тегів JSP полягає в тому, що вони в основному дозволяють тегувати загальну розмітку, а потім переробляти її на вміст вашого серця.

JSP Tag FilesTilesпринаймні узурпували такі речі, як тощо, принаймні для мене. Мені здається, що їх набагато простіше використовувати, оскільки єдина структура - це те, що ти їй надаєш, нічого не задумане. Крім того, ви можете використовувати файли тегів JSP для інших речей (наприклад, фрагмент деталей користувача вище).

Ось приклад, схожий на DisplayTag, який я робив, але все це робиться за допомогою файлів тегів (і Stripesфреймворку, це s: теги ..). Це призводить до таблиці рядків, змінних кольорів, навігації по сторінці тощо:

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

Звичайно, теги працюють з JSTL tags(як c:if, і т.д.). Єдине, що ви не можете зробити в тілі тегу файлу тегів, це додати код скрипту Java, але це не стільки обмеження, скільки ви можете подумати. Якщо мені потрібні речі з сценаріїв, я просто вкладаю логіку в тег і відпускаю тег. Легко.

Отже, файли тегів можуть бути майже будь-якими, якими ви їх хочете. На самому базовому рівні це просте вирізання та наклеювання рефакторингу. Візьміть шматок макета, виріжте його, зробіть просту параметризацію та замініть на виклик тегів.

На більш високому рівні ви можете робити складні речі, наприклад, цей тег таблиці, який я маю тут.


34
Дякую за це Це найкращий підручник, який я міг знайти у файлах тегів JSP, які для мене були чудовими з JSF. Бажаю, щоб я міг дати більше одного голосу.
digitaljoel

66
+ 40 мільйонів. Дякую, що ви пояснили це в 50000 разів краще, ніж будь-який лукавий підручник, який я знайшов. Виходячи зі світу Rails та відсутня ЄРБ, це саме те, що мені потрібно. Вам слід написати блог.
cbmeeks

2
Дійсно приємний підручник. Не могли б ви поділитися з нами кодом для створеного вами табличного тегу? Я створив його сам деякий час тому, але ваш підхід кращий.
Тіаго Дуарте

4
Якщо ви створюєте тег файлу тегів, вміст цього тегу у файлі JSP не може містити код сценарію: <t: mytag> тут немає коду сценарію </ t: mytag>. Але у файлі тегів, що реалізує сам тег, він може мати весь код скрипту, який ви хочете, як і будь-який JSP.
Вілл Хартунг

4
Примітка - здається, порядок тегів важливий; jsp: атрибут повинен бути перед jsp: body або ви отримаєте помилку. Також мені довелося встановити відповідний тег @attribute, щоб він відповідав jsp: invoke, щоб уникнути чергової помилки. Використання GlassFish 3.2.2
Ryan

21

Я зробив досить просто, бібліотека тегів успадкування шаблонів JSP шаблонів у стилі Джанго. https://github.com/kwon37xi/jsp-template-inheritance

Я думаю, що це легко керувати макетами без вивчення кривої.

приклад коду:

base.jsp: макет

<%@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: вміст

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

На основі тієї ж основної ідеї, що і в @Will Hartung , ось мій чарівний двигун шаблону, що розширюється. Він навіть включає документацію та приклад :-)

WEB-INF / теги / 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

Використовуйте плитку . Це врятувало мені життя.

Але якщо ви не можете, є тег включення , що робить його схожим на php.

Тег body може насправді не робити те, що вам потрібно, якщо ви не маєте надпростий вміст. Тег body використовується для визначення тіла певного елемента. Погляньте на цей приклад :

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

Ви вказуєте ім'я елемента, будь-які атрибути, які може мати цей елемент ("lang" у цьому випадку), а потім текст, що йде в ньому, - body. Так що якщо

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

Тоді вихід був би

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


0

додати залежності для використання <% @ tag description = "Шаблон сторінки користувача" 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

Я знаю, що ця відповідь приходить через роки після факту, і вже є чудова відповідь JSP Вілла Хартунга, але є Facelets, вони навіть згадуються у відповідях із пов'язаного питання в оригінальному запитанні.

Опис тегів фасеток SO

Facelets - це технологія перегляду на основі XML для рамки JavaServer Faces. Розроблений спеціально для JSF, Facelets призначений для простішої та потужнішої альтернативи поглядів на основі JSP. Спочатку окремим проектом ця технологія була стандартизована як частина JSF 2.0 та Java-EE 6 і застаріла JSP. Майже всі бібліотеки компонентів, націлених на JSF 2.0, вже не підтримують JSP, а лише Facelets.

На жаль, найкращий простий опис підручника я знайшов у Вікіпедії, а не на навчальному сайті. Насправді розділ, що описує шаблони, навіть відповідає тому, що було задано оригінальним запитанням.

У зв'язку з тим, що Java-EE 6 застарів JSP, я б рекомендував переходити з Facelet, незважаючи на те, що, схоже, може знадобитися більше, щоб не отримати жодного посилення над JSP.


Java EE 6 не застаріло JSP, просто застаріло, використовуючи JSP як технологію перегляду для JSF.
Райан

@Ryan Оскільки в цьому випадку обидва говорили про технологію перегляду, що не так у тому, що сказали, що це знецінило її?
Fering

Питання не має нічого спільного з JSF. Йдеться про чистий JSP. Ваша відповідь - використовувати Facelets, який призначений для JSF.
Райан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.