Trik JSP untuk membuat templating lebih mudah?

305

Di tempat kerja saya ditugaskan untuk mengubah banyak HTMLfile menjadi JSPproyek sederhana . Ini benar-benar semua statis, tidak ada logika sisi server untuk diprogram. Saya harus menyebutkan bahwa saya benar-benar baru di Jawa. File JSP tampaknya membuatnya mudah untuk bekerja dengan menyertakan umum dan variabel, seperti PHP, tapi saya ingin tahu cara sederhana untuk mendapatkan sesuatu seperti warisan template ( Djangogaya) atau setidaknya bisa memiliki file base.jsp yang berisi header dan footer, jadi saya bisa memasukkan konten nanti.

Ben Lings tampaknya menawarkan beberapa harapan dalam jawabannya di sini: pewarisan template JSP Bisakah seseorang menjelaskan cara mencapai ini?

Mengingat saya tidak punya banyak waktu, saya pikir perutean dinamis sedikit banyak, jadi saya senang hanya memiliki URL peta langsung ke .jspfile, tapi saya terbuka untuk saran.

Terima kasih.

sunting: Saya tidak ingin menggunakan perpustakaan eksternal, karena itu akan meningkatkan kurva belajar untuk saya dan orang lain yang bekerja di proyek, dan perusahaan tempat saya bekerja telah dikontrak untuk melakukan ini.

Suntingan lain: Saya tidak yakin apakah JSP tagsakan berguna karena konten saya tidak benar-benar memiliki variabel template. Yang saya butuhkan adalah cara untuk dapat melakukan ini:

base.html:

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

somepage.html

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

dengan output menjadi:

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

Saya pikir ini akan memberi saya fleksibilitas yang cukup untuk melakukan semua yang saya butuhkan. Itu bisa dicapai dengan includestetapi kemudian saya akan membutuhkan bagian atas dan bawah untuk setiap bungkus, yang agak berantakan.

Scott
sumber

Jawaban:

682

Seperti yang disarankan skaffman , File Tag JSP 2.0 adalah lutut lebah.

Mari kita ambil contoh sederhana Anda.

Masukkan yang berikut ini WEB-INF/tags/wrapper.tag

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

Sekarang di example.jsphalaman Anda :

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

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

Itu persis seperti yang Anda pikirkan.


Jadi, mari kita bahas hal yang sedikit lebih umum. 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>

Untuk menggunakan ini:

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

Apa yang membelikanmu? Banyak sekali, tapi itu menjadi lebih baik ...


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>

Untuk menggunakan ini: (anggap kita memiliki variabel pengguna dalam permintaan)

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

Tapi ternyata Anda suka menggunakan blok detail pengguna di tempat lain. Jadi, kita akan refactor saja. 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/>

Sekarang contoh sebelumnya menjadi:

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

Keindahan file Tag JSP adalah pada dasarnya Anda dapat menandai markup generik dan kemudian mengubahnya menjadi isi hati Anda.

JSP Tag Filespunya banyak hal yang direbut seperti Tilesdll, setidaknya untuk saya. Saya menemukan mereka lebih mudah digunakan karena satu-satunya struktur adalah apa yang Anda berikan, tidak ada yang terbentuk sebelumnya. Plus Anda dapat menggunakan file tag JSP untuk hal lain (seperti fragmen detail pengguna di atas).

Berikut adalah contoh yang mirip dengan DisplayTag yang telah saya lakukan, tetapi ini semua dilakukan dengan Tag Files (dan Stripeskerangka kerjanya , itulah s: tag ..). Ini menghasilkan tabel baris, warna bergantian, navigasi halaman, dll:

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

Tentu saja tag bekerja dengan JSTL tags(seperti c:if, dll.). Satu-satunya hal yang tidak dapat Anda lakukan dalam tubuh tag file tag adalah menambahkan kode Java scriptlet, tetapi ini tidak sebanyak keterbatasan yang Anda pikirkan. Jika saya membutuhkan hal-hal scriptlet, saya hanya memasukkan logika ke tag dan drop tag masuk Mudah.

Jadi, tag file bisa menjadi apa pun yang Anda inginkan. Pada tingkat paling dasar, ini sederhana dan paste refactoring. Ambil sepotong tata letak, hentikan, lakukan beberapa parameterisasi sederhana, dan ganti dengan permintaan tag.

Pada level yang lebih tinggi, Anda dapat melakukan hal-hal canggih seperti tag tabel yang saya miliki di sini.

Will Hartung
sumber
34
Terima kasih untuk ini. Ini adalah tutorial terbaik yang bisa saya temukan pada file tag JSP, yang bagus untuk saya yang berasal dari JSF. Seandainya saya bisa memberikan lebih dari satu suara.
digitaljoel
66
+ 40 juta. Terima kasih telah menjelaskannya 50.000 kali lebih baik daripada tutorial buruk yang saya temukan. Datang dari dunia Rails dan hilang ERB, inilah yang saya butuhkan. Anda harus menulis blog.
cbmeeks
2
Tutorial yang sangat bagus. Bisakah Anda berbagi dengan kami kode untuk tag tabel yang Anda buat? Saya membuat sendiri beberapa waktu lalu tetapi pendekatan Anda lebih baik.
Thiago Duarte
4
Jika Anda membuat tag file tag, konten tag itu dalam file JSP tidak dapat memiliki kode skrip: <t: mytag> tidak ada kode skrip di sini </ t: mytag>. Tetapi dalam file tag yang mengimplementasikan tag itu sendiri, itu dapat memiliki semua kode skrip yang Anda inginkan, seperti JSP apa pun.
Will Hartung
4
Catatan - sepertinya urutan tag itu penting; atribut jsp: harus ada sebelum jsp: tubuh atau Anda akan mendapatkan kesalahan. Saya juga harus menetapkan tag @attribute yang sesuai untuk mencocokkan jsp: invoke untuk menghindari kesalahan lain. Menggunakan GlassFish 3.2.2
Ryan
21

Saya membuatnya cukup mudah, perpustakaan tag warisan JSP style Django style. https://github.com/kwon37xi/jsp-template-inheritance

Saya pikir itu mudah untuk mengelola tata letak tanpa kurva belajar.

contoh kode:

base.jsp: tata letak

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

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

Berdasarkan ide dasar yang sama dengan jawaban @Will Hartung , berikut ini adalah mesin templat extensible one-tag ajaib saya. Bahkan termasuk dokumentasi dan contoh :-)

WEB-INF / tag / 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
sumber
4

Gunakan ubin . Itu menyelamatkan hidupku.

Tetapi jika Anda tidak bisa, ada tag include , yang membuatnya mirip dengan php.

Tag tubuh mungkin tidak benar-benar melakukan apa yang Anda inginkan, kecuali jika Anda memiliki konten yang sangat sederhana. Tag tubuh digunakan untuk mendefinisikan tubuh elemen yang ditentukan. Lihatlah contoh ini :

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

Anda menentukan nama elemen, atribut apa pun yang mungkin dimiliki elemen ("lang" dalam kasus ini), dan kemudian teks yang masuk di dalamnya - tubuh. Jadi jika

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

Maka hasilnya akan menjadi

<h1 lang="fr">Heading in French</h1>
geowa4
sumber
0

tambahkan dependecys untuk digunakan <% @ tag description = "Templat Halaman Pengguna" 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>
Juan Silupú Maza
sumber
-1

Saya tahu jawaban ini datang bertahun-tahun setelah fakta dan sudah ada jawaban JSP yang bagus oleh Will Hartung, tetapi ada Facelet, mereka bahkan disebutkan dalam jawaban dari pertanyaan terkait di pertanyaan awal.

Keterangan tag Facelets SO

Facelets adalah teknologi tampilan berbasis XML untuk kerangka JavaServer Faces. Dirancang khusus untuk JSF, Facelets dimaksudkan untuk menjadi alternatif yang lebih sederhana dan lebih kuat untuk tampilan berbasis JSP. Awalnya proyek terpisah, teknologi ini distandarisasi sebagai bagian dari JSF 2.0 dan Java-EE 6 dan telah usang JSP. Hampir semua pustaka komponen yang ditargetkan JSF 2.0 tidak mendukung JSP lagi, tetapi hanya Facelet.

Sayangnya deskripsi tutorial sederhana terbaik yang saya temukan adalah di Wikipedia dan bukan situs tutorial. Bahkan bagian yang menggambarkan templat bahkan melakukan apa yang diminta pertanyaan asli.

Karena fakta bahwa Java-EE 6 telah usang JSP saya akan merekomendasikan pergi dengan Facelet meskipun fakta bahwa mungkin ada lebih banyak diperlukan untuk sedikit atau tidak ada keuntungan atas JSP.

Fering
sumber
Java EE 6 belum meninggalkan JSP, hanya berhenti menggunakan JSP sebagai teknologi tampilan untuk JSF.
Ryan
@Ryan Karena dalam kasus ini keduanya berbicara tentang teknologi tampilan, apa yang salah dengan mengatakan itu tidak berlaku lagi?
Fering
Pertanyaannya tidak ada hubungannya dengan JSF. Ini tentang JSP murni. Jawaban Anda adalah menggunakan Facelets, yang untuk JSF.
Ryan