Уловки 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 Tag Files - это колени пчелы.

Давайте возьмем ваш простой пример.

Поместите следующее в 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 Filesв значительной степени узурпировали такие вещи, как Tilesи т.д., по крайней мере, для меня. Я считаю, что их гораздо проще использовать, поскольку единственная структура - это то, что вы ей даете, ничего предвзятого. Кроме того, вы можете использовать файлы тегов JSP для других целей (например, фрагмент сведений о пользователе выше).

Вот пример, который похож на DisplayTag, который я сделал, но все это делается с помощью Tag Files (и Stripesфреймворка, это s: tags ..). Это приводит к таблице строк, чередующихся цветов, навигации по страницам и т.д .:

<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 , и т. Д.). Единственное, что вы не можете сделать в теле тега file tag, это добавить код Java-скриптлета, но это не такое ограничение, как вы думаете. Если мне нужно что-то из скриплетов, я просто добавляю логику в тег и опускаю тег. Легко.

Таким образом, файлы тегов могут быть практически такими, какими вы хотите их видеть. На самом базовом уровне это простой рефакторинг для вырезания и вставки. Возьмите кусок макета, вырежьте его, выполните простую параметризацию и замените его вызовом тега.

На более высоком уровне вы можете делать сложные вещи, как этот тег таблицы, который я здесь.

Уилл Хартунг
источник
34
Спасибо за это. Это лучшее учебное пособие, которое я смог найти по файлам тегов JSP, которые мне очень понравились из JSF. Жаль, что я не мог дать больше одного голоса.
digitaljoel
66
+ 40million. Спасибо, что объяснили это в 50000 раз лучше, чем любой дерьмовый урок, который я нашел. Исходя из мира Rails и отсутствующего ERB, это именно то, что мне нужно. Вы должны написать блог.
cbmeeks
2
Действительно хороший учебник. Не могли бы вы поделиться с нами кодом для тега таблицы, который вы сделали? Я сам его создал некоторое время назад, но ваш подход лучше.
Тьяго Дуарте
4
Если вы создаете тег файла тега, содержимое этого тега в файле JSP не может иметь код скриптлета: <t: mytag> здесь нет кода скриптлета </ t: mytag>. Но в файле тегов, реализующем сам тег, в котором может быть весь код скриптлета, который вы хотите, как и в любом JSP.
Уилл Хартунг
4
Примечание - похоже, порядок тегов важен; Атрибут jsp: должен предшествовать jsp: body, иначе вы получите ошибку. Также мне пришлось установить соответствующий тег @attribute для соответствия jsp: invoke, чтобы избежать другой ошибки. Используя GlassFish 3.2.2
Райан
21

Я сделал довольно легко, библиотеку тегов наследования JSP-шаблонов в стиле Django. 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>
KwonNam
источник
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);
    }
%>
amichair
источник
4

Используйте плитки . Это спасло мою жизнь.

Но если вы не можете, есть тег include , который делает его похожим на 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»), а затем текст, который идет в нем, - тело. Так что если

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

Тогда вывод будет

<h1 lang="fr">Heading in French</h1>
geowa4
источник
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, они даже упоминаются в ответах на связанный вопрос в оригинальном вопросе.

Описание тегов 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, я бы порекомендовал использовать Facelets, несмотря на тот факт, что может показаться, что может потребоваться больше для того, чтобы немного выиграть или не получить преимущества над JSP.

Fering
источник
Java EE 6 не осуждает JSP, просто устарела с использованием JSP в качестве технологии представления для JSF.
Райан
@Ryan Так как в этом случае оба говорили о технологии просмотра, что плохого в том, что она устарела?
Fering
Вопрос не имеет ничего общего с JSF. Речь идет о чистом JSP. Ваш ответ - использовать Facelets для JSF.
Райан