Вопрос-ответ

JSP tricks to make templating easier?

JSP-хитрости, упрощающие создание шаблонов?

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

Бен Линг, кажется, дает некоторую надежду в своем ответе здесь: Наследование шаблонов JSP Кто-нибудь может объяснить, как этого добиться?

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

Спасибо.

редактировать: я не хочу использовать какие-либо внешние библиотеки, потому что это увеличило бы время обучения для меня и других, кто работает над проектом, а компания, в которой я работаю, получила контракт на это.

Еще одно редактирование: я не уверен, что JSP tags будет полезно, потому что в моем контенте на самом деле нет переменных шаблона. Что мне нужно, так это способ сделать это:

base.html:

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

somepage.html

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

с выводом:

<html><body>
<h3>Welcome</h3>
</body></html>

Я думаю, это дало бы мне достаточно универсальности, чтобы делать все, что мне нужно. Этого можно было бы достичь с помощью includes но тогда мне понадобились бы верхние и нижние включения для каждой оболочки, что немного запутанно.

Переведено автоматически
Ответ 1

Как предложил Скаффман, файлы тегов 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>
<h3>Welcome</h3>
</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">
<h3>Welcome</h3>
</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">
<h3>Welcome ${userName}</h3>
</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, который я сделал, но все это делается с помощью файлов тегов (и 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 (like c:if и т.д.). Единственное, что вы не можете сделать в теле тега tag file, это добавить код Java-скриптлета, но это не такое большое ограничение, как вы могли подумать. Если мне нужны скриплеты, я просто помещаю логику в тег и добавляю тег. Легко.

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

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

Ответ 2

Я создал довольно простую библиотеку тегов наследования шаблонов 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>

<h3>Head</h3>
<div>
<layout:block name="header">
header
</layout:block>
</div>

<h3>Contents</h3>
<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>

просмотреть.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>
Ответ 3

Основываясь на той же базовой идее, что и в ответе @ Уилла Хартунга, вот мой волшебный расширяемый шаблонизатор с одним тегом. Он даже включает документацию и пример :-)

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>
<h3>${title}</h3>
<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

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

Но если вы не можете, есть тег 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"), а затем содержащийся в нем текст - body . Итак, если


  • content.headerName = h3,

  • content.lang = fr, и

  • content.body = Heading in French

Тогда результат будет следующим

<h3 lang="fr">Heading in French</h3>
java jsp inheritance