テンプレート化を容易にするための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

以下のようskaffmanが示唆され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 tagsc:ifなど)で動作します。タグファイルタグの本文内で実行できない唯一のことは、Javaスクリプトレットコードを追加することですが、これは思ったほどの制限ではありません。スクリプトレットのものが必要な場合は、ロジックをタグに挿入して、タグをドロップします。簡単です。

したがって、タグファイルは、ほとんどの場合、希望どおりのものにすることができます。最も基本的なレベルでは、単純なカットアンドペーストのリファクタリングです。レイアウトのチャンクを取得し、それを切り取り、いくつかの単純なパラメーター化を行い、タグの呼び出しに置き換えます。

より高いレベルでは、私がここに持っているこのテーブルタグのような洗練されたことができます。


34
これをありがとう。これは、JSPタグファイルで見つけることができる最高のチュートリアルであり、JSFから来た私にとっては非常に良かったです。私は複数票を投じることができればいいのに。
digitaljoel 2010年

66
+4000万。私が見つけたくだらないチュートリアルより50,000倍説明してくれてありがとう Railsの世界から来て、ERBがないため、これがまさに私が必要としていることです。ブログを書いてください。
cbmeeks

2
本当にいいチュートリアル。作成したテーブルタグのコードを教えてください。少し前に自分で作成しましたが、あなたのアプローチの方が優れています。
チアゴドゥアルテ

4
タグファイルタグを作成する場合、JSPファイルのそのタグのコンテンツにスクリプトレットコードを含めることはできません:<t:mytag>スクリプトレットコードはありません</ t:mytag>。ただし、タグ自体を実装するタグファイル内には、JSPのように、必要なすべてのスクリプトレットコードを含めることができます。
ウィルハートン

4
注-タグの順序は重要なようです。jsp:attributeはjsp:bodyの前に置く必要があります。そうしないと、エラーが発生します。また、jsp:invokeに一致するように対応する@attributeタグを設定して、別のエラーを回避する必要がありました。GlassFish 3.2.2の使用
Ryan

21

非常に簡単なDjangoスタイルの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 / 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);
    }
%>

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>


0

使用する依存関係を追加<%@ tag description = "User Page template" 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

私はこの答えが事実の数年後であることを知っており、Will HartungによるすばらしいJSP回答がすでにありますが、Faceletsがあり、元の質問のリンクされた質問からの回答でさえ言及されています。

Facelets SOタグの説明

Faceletsは、JavaServer Facesフレームワーク用のXMLベースのビューテクノロジーです。JSF専用に設計されたFaceletsは、JSPベースのビューに代わる、よりシンプルで強力な代替手段となることを目的としています。当初は別のプロジェクトでしたが、このテクノロジーはJSF 2.0およびJava-EE 6の一部として標準化され、JSPは非推奨になりました。ほとんどすべてのJSF 2.0ターゲットコンポーネントライブラリはJSPをサポートせず、Faceletsのみをサポートします。

残念ながら、私が見つけた最もわかりやすいチュートリアルの説明は、チュートリアルサイトではなくWikipediaにありました。実際、テンプレートを説明するセクションは、元の質問が求めていた内容にも沿っています。

Java-EE 6はJSPを非推奨にしたという事実のため、JSPをほとんどまたはまったく獲得する必要がないように見えるかもしれないという事実にもかかわらず、Faceletsを使用することをお勧めします。


Java EE 6はJSPを非推奨にせず、JSFのビューテクノロジーとしてのJSPの使用を非推奨にしました。
ライアン

@Ryanこの場合、両方ともビュー技術について話していたので、それを非推奨にしたと言ったことの何が問題になっていますか?
フェリング

この質問はJSFとは関係ありません。純粋なJSPについてです。あなたの答えは、JSF用のFaceletsを使用することです。
ライアン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.