如果要自訂標籤,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">
網址 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 中可以使用 out
、config
、request
、response
、session
、application
、jspContext
等隱含物件,其中 jspContext
在轉譯之後,實際上則是 javax.servlet.jsp.JspContext
物件。
所以,Tag File 在 JSP 中,並不是靜態包含或動態包含,在 Tag File 中撰寫 Scriplet 的話,其中的區域變數也不可能與 JSP 中 Scriptlet 直接溝通。
JspContext
是 PageContext
的父類別,JspContext
上定義的 API 不像 PageContext
有使用到Servlet API,原本在設計上希望 JSP 的相關實現可以不依賴特定技術(例如Servlet),所以才會有 JspContext
這個父類別的存在。