Thủ thuật JSP để làm cho templating dễ dàng hơn?


305

Tại nơi làm việc, tôi được giao nhiệm vụ biến một loạt các HTMLtập tin thành một JSPdự án đơn giản . Đó thực sự là tất cả tĩnh, không có logic máy chủ để lập trình. Tôi nên đề cập đến tôi hoàn toàn mới đối với Java. Các tệp JSP dường như làm cho nó dễ dàng làm việc với các biến bao gồm và các biến chung, giống như PHP, nhưng tôi muốn biết một cách đơn giản để có được một cái gì đó như thừa kế mẫu ( Djangokiểu) hoặc ít nhất có thể có tệp base.jsp chứa tiêu đề và chân trang, vì vậy tôi có thể chèn nội dung sau.

Ben Lings dường như cung cấp một số hy vọng trong câu trả lời của mình ở đây: Kế thừa mẫu của JSP Ai đó có thể giải thích làm thế nào để đạt được điều này?

Vì tôi không có nhiều thời gian nên tôi nghĩ rằng định tuyến động là hơi nhiều, vì vậy tôi rất vui khi chỉ có URL ánh xạ trực tiếp vào .jspcác tệp, nhưng tôi mở để đề xuất.

Cảm ơn.

chỉnh sửa: Tôi không muốn sử dụng bất kỳ thư viện bên ngoài nào, vì nó sẽ tăng thời gian học tập cho bản thân tôi và những người khác làm việc trong dự án và công ty tôi làm việc đã được ký hợp đồng để thực hiện điều này.

Một chỉnh sửa khác: Tôi không chắc liệu JSP tagsnó có hữu ích không vì nội dung của tôi không thực sự có bất kỳ biến mẫu nào. Những gì tôi cần là một cách để có thể làm điều này:

base.html:

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

somepage.html

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

với đầu ra là:

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

Tôi nghĩ rằng điều này sẽ cho tôi đủ linh hoạt để làm mọi thứ tôi cần. Nó có thể đạt được với includesnhưng sau đó tôi sẽ cần một đỉnh và đáy bao gồm cho mỗi trình bao bọc, đó là loại lộn xộn.

Câu trả lời:


682

Như skaffman đã đề xuất , Tệp thẻ JSP 2.0 là đầu gối của con ong.

Hãy lấy ví dụ đơn giản của bạn.

Đặt những thứ sau vào WEB-INF/tags/wrapper.tag

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

Bây giờ trong example.jsptrang của bạn :

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

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

Điều đó làm chính xác những gì bạn nghĩ nó làm.


Vì vậy, hãy mở rộng điều đó thành một cái gì đó tổng quát hơn một chút. 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>

Để sử dụng điều này:

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

Cái đó mua gì cho bạn? Thực sự rất nhiều, nhưng nó thậm chí còn tốt hơn ...


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>

Để sử dụng điều này: (giả sử chúng tôi có một biến người dùng trong yêu cầu)

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

Nhưng nó biến bạn muốn sử dụng khối chi tiết người dùng đó ở những nơi khác. Vì vậy, chúng tôi sẽ cấu trúc lại nó. 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/>

Bây giờ ví dụ trước trở thành:

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

Cái hay của các tệp Thẻ JSP là về cơ bản cho phép bạn gắn thẻ đánh dấu chung và sau đó cấu trúc lại nó theo nội dung trái tim của bạn.

JSP Tag Filescó khá nhiều thứ chiếm đoạt như Tilesvv, ít nhất là đối với tôi. Tôi thấy chúng dễ sử dụng hơn nhiều vì cấu trúc duy nhất là những gì bạn cung cấp cho nó, không có gì có trước. Ngoài ra, bạn có thể sử dụng các tệp thẻ JSP cho những thứ khác (như đoạn chi tiết người dùng ở trên).

Đây là một ví dụ tương tự như DisplayTag mà tôi đã thực hiện, nhưng tất cả đều được thực hiện với Tệp thẻ (và Stripeskhung, đó là s: tags ..). Điều này dẫn đến một bảng các hàng, màu sắc xen kẽ, điều hướng trang, v.v .:

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

Tất nhiên các thẻ làm việc với JSTL tags(như c:if, v.v.). Điều duy nhất bạn không thể làm trong phần thân của thẻ tệp thẻ là thêm mã scriptlet Java, nhưng điều này không quá giới hạn như bạn nghĩ. Nếu tôi cần công cụ scriptlet, tôi chỉ cần đặt logic vào một thẻ và thả thẻ vào. Dễ dàng.

Vì vậy, các tệp thẻ có thể là khá nhiều bất cứ điều gì bạn muốn chúng là. Ở cấp độ cơ bản nhất, đó là tái cấu trúc cắt và dán đơn giản. Lấy một đoạn bố cục, cắt nó ra, thực hiện một số tham số đơn giản và thay thế nó bằng một lệnh gọi thẻ.

Ở cấp độ cao hơn, bạn có thể thực hiện những điều tinh vi như thẻ bảng này tôi có ở đây.


34
Cảm ơn vì điều đó. Đó là hướng dẫn tốt nhất mà tôi có thể tìm thấy trên các tệp thẻ JSP, rất phù hợp với tôi đến từ JSF. Ước gì tôi có thể cho nhiều hơn một phiếu bầu.
Digitaljoel

66
+ 40 triệu. Cảm ơn bạn đã giải thích nó tốt hơn 50.000 lần so với bất kỳ hướng dẫn nhảm nhí nào tôi đã tìm thấy. Đến từ thế giới Rails và thiếu ERB, đây chính xác là thứ tôi cần. Bạn nên viết một blog.
cbmeek

2
Hướng dẫn thực sự tốt đẹp. Bạn có thể chia sẻ với chúng tôi mã cho thẻ bảng mà bạn đã tạo không? Tôi đã tự tạo một cái trước đây nhưng cách tiếp cận của bạn tốt hơn.
Thiago Duarte

4
Nếu bạn tạo thẻ tệp thẻ, nội dung của thẻ đó trong tệp JSP không thể có mã scriptlet: <t: mytag> không có mã scriptlet ở đây </ t: mytag>. Nhưng trong tệp thẻ thực hiện chính thẻ đó, có thể có tất cả mã scriptlet mà bạn muốn, giống như bất kỳ tệp JSP nào.
Will Hartung

4
Lưu ý - có vẻ như thứ tự của các thẻ là quan trọng; jsp: thuộc tính phải đến trước jsp: body hoặc bạn sẽ gặp lỗi. Ngoài ra, tôi phải đặt một thẻ @attribution tương ứng để khớp với jsp: invoke để tránh một lỗi khác. Sử dụng GlassFish 3.2.2
Ryan

21

Tôi đã thực hiện khá dễ dàng, thư viện thẻ kế thừa Mẫu Django. https://github.com/kwon37xi/jsp-template-inherribution

Tôi nghĩ rằng nó dễ dàng để quản lý bố trí mà không cần học đường cong.

mã ví dụ:

base.jsp: bố cục

<%@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: nội dung

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

Dựa trên cùng một ý tưởng cơ bản như trong câu trả lời của @Will Hartung , đây là công cụ mẫu mở rộng một thẻ ma thuật của tôi. Nó thậm chí bao gồm tài liệu và một ví dụ :-)

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

Sử dụng gạch . Nó đã cứu mạng tôi.

Nhưng nếu bạn không thể, có thẻ bao gồm , làm cho nó tương tự như php.

Thẻ body có thể không thực sự làm những gì bạn cần, trừ khi bạn có nội dung siêu đơn giản. Thẻ body được sử dụng để xác định phần thân của một phần tử được chỉ định. Hãy xem ví dụ này :

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

Bạn chỉ định tên thành phần, bất kỳ thuộc tính nào mà thành phần đó có thể có ("lang" trong trường hợp này), và sau đó văn bản đi trong đó - phần thân. Vì vậy nếu

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

Sau đó, đầu ra sẽ là

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


0

thêm phụ thuộc để sử dụng <% @ tag description = "Mẫu trang người dùng" 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

Tôi biết câu trả lời này sẽ xuất hiện sau nhiều năm và thực tế đã có câu trả lời tuyệt vời của Will Hartung, nhưng có Facelets, chúng thậm chí còn được đề cập trong các câu trả lời từ câu hỏi được liên kết trong câu hỏi ban đầu.

Facelets mô tả thẻ SO

Facelets là một công nghệ khung nhìn dựa trên XML cho khung công tác JavaServer Faces. Được thiết kế dành riêng cho JSF, Facelets được dự định là một sự thay thế đơn giản và mạnh mẽ hơn cho các khung nhìn dựa trên JSP. Ban đầu là một dự án riêng biệt, công nghệ này đã được tiêu chuẩn hóa như là một phần của JSF 2.0 và Java-EE 6 và đã không còn dùng được nữa. Hầu như tất cả các thư viện thành phần được nhắm mục tiêu của JSF 2.0 không hỗ trợ JSP nữa mà chỉ hỗ trợ Facelets.

Đáng buồn là mô tả hướng dẫn đơn giản tốt nhất tôi tìm thấy là trên Wikipedia và không phải là một trang web hướng dẫn. Trong thực tế, phần mô tả các mẫu thậm chí thực hiện dọc theo dòng của câu hỏi ban đầu được yêu cầu.

Do thực tế là Java-EE 6 đã không còn sử dụng JSP, tôi khuyên bạn nên sử dụng Facelets mặc dù thực tế là có vẻ như có nhiều yêu cầu hơn đối với việc không đạt được nhiều hơn so với JSP.


Java EE 6 đã không phản đối JSP, chỉ không dùng nữa khi sử dụng JSP làm công nghệ xem cho JSF.
Ryan

@Ryan Vì trong trường hợp này cả hai đều nói về công nghệ xem, có gì sai khi nói rằng nó không được chấp nhận?
Fering

Câu hỏi không liên quan gì đến JSF. Đó là về JSP thuần túy. Câu trả lời của bạn là sử dụng Facelets, dành cho JSF.
Ryan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.