簡介 Tag File


如果要自訂標籤,Tag File 是最簡單的方式,即使是不會 Java 的網頁設計人員也有能力自訂 Tag File。

假設有以下的 JSP 片段:

<%
    List<String> errors = (List<String>) request.getAttribute("errors");
    if (errors != null) {
%>
        <ul style="color: rgb(255, 0, 0);">
<%
       errors.forEach(error -> out.println("<li>" + error + "</li>"));
       out.println("</ul>");
    }
%>

這個片段的作用,在於使用者新增書籤時沒有填寫必要欄位時而回到原網頁時,會出現相關的錯誤訊息,這些錯誤訊息是收集在一個 List 物件中,並在 request 設定 errors 屬性後傳遞過來。由於你已經學過 JSTL 了,可以將這個 Scriptlet 與 HTML 夾雜的片段改為:

<c:if test="${requestScope.errors != null}">
    <ul style="color: rgb(255, 0, 0);">
        <c:forEach var="error" items="${requestScope.errors}">
            <li>${error}</li>
        </c:forEach>
    </ul>
</c:if>

現在即使是網頁設計人員,也可以看懂並依需求修改這個片段了。然而,這種錯誤訊息的呈現方式,也許並不僅出現在一個網頁,其它網頁也需要同樣的片段。每次 都得複製貼上同樣的片段還不成問題,但將來要修改外觀樣式時才是一大麻煩。網頁設計人員也許會想說,這樣的片段若可以像是 <html:errrors> 這樣的標籤存在就好了。

如果網頁設計人員知道可以使用 Tag File,那這個需求就解決了。他們可以撰寫一個副檔名為 .tag 的檔案,把它們放在 WEB-INF/tags 底下,內容如下:

Errors.tag

<%@tag description="顯示錯誤訊息的標籤" pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:if test="${requestScope.errors != null}">
    <ul style="color: rgb(255, 0, 0);">
        <c:forEach var="error" items="${requestScope.errors}">
            <li>${error}</li>
        </c:forEach>
    </ul>
</c:if> 

在這邊你看到了 tag 指示元素,它就像是 JSP 的 page 指示元素,用來告知容器如何轉譯這個 Tag File。description 只是一段文字描述,用來說明這個 Tag File 的作用。pageEncoding 屬性告知容器在轉譯 Tag File 時使用的編碼。Tag File 中可以使用 taglib 指示元素引用其他自訂標籤庫,可以在 Tag File 中使用 JSTL。基本上,JSP 檔案中可以使用的 EL 或 Scriptlet,在 Tag File 中也可以使用。

在需要這個 Tag File 的 JSP 頁面中,可以使用 taglib 指示元素的 prefix 定義前置名稱,以及使用 tagdir 屬性定義 Tag File 的位置:

<%@taglib prefix="html" tagdir="/WEB-INF/tags" %>

接著就可以在 JSP 中需要呈現錯誤訊息的地方,使用 <html:Errors/> 標籤來代替先前呈現錯誤訊息的片段。例如:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib  prefix="html" tagdir="/WEB-INF/tags" %>
<!DOCTYPE html>
<html>
    <head>
        <title>新增書籤</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <html:Errors/>
        <form method="post" action="add">
            網址&nbsp;http:// <input name="url" value="${param.url}"><br>
            網頁名稱:<input name="title" value="${param.title}"><br>
            分  類:<input type="text" name="category" value="${param.category}"><br>
            <input value="送出" type="submit"><br>
        </form>
    </body>
</html>

當然!使用這個自訂的<html:Errors/> 標籤有個假設前題。錯誤訊息是收集在一個 List 物件中,在 request 中設定 errors 屬性後傳遞過來。除非是大家都公認的標準,否則自訂標籤必然與應用程式有某種程度的相依性,在自訂標籤前,於使用的方便性及相依性之間必須作出考量。

注意!雖然 tagdir 可以指定 Tag File 的位置,但事實上只能指定 /WEB-INF/tags 的子資料夾,也就是說,以 tagdir 屬性設定的話,Tag File 就只能放在 /WEB-INF/tags 或子資料夾之中。

前面提過 Tag File 會被容器轉譯,實際上是轉譯為 javax.servlet.jsp.tagext.SimpleTagSupport 的子類別。

以 Tomcat 為例,Errors.tag 轉譯後的類別原始碼名稱是 Errors_tag.java。在 Tag File 中可以使用 outconfigrequestresponsesessionapplicationjspContext 等隱含物件,其中 jspContext 在轉譯之後,實際上則是 javax.servlet.jsp.JspContext 物件。

所以,Tag File 在 JSP 中,並不是靜態包含或動態包含,在 Tag File 中撰寫 Scriplet 的話,其中的區域變數也不可能與 JSP 中 Scriptlet 直接溝通。

JspContextPageContext 的父類別,JspContext 上定義的 API 不像 PageContext 有使用到Servlet API,原本在設計上希望 JSP 的相關實現可以不依賴特定技術(例如Servlet),所以才會有 JspContext 這個父類別的存在。