Java for Web学习笔记(三六):自定义tag(4)自定义Tag文件

Tag文件中的Directives

Tag文件和jsp很相似,也有不同,主要的不同在directives。Jsp的directive有include,taglib和page,但tag中有include和taglib,没有page,此外还提供了tag,tag替代了jsp中的page,并提供了tld文件中<tag>的配置功能。

tag directive有下面属性,均为可选:

  • pageEncoding:同page中的含义,定义tag输出的编码。
  • isELIgnored:同page中的含义,要求container不计算EL表达式,缺省为false。
  • language:同page中的含义,目前只支持java。
  • deferredSyntaxAllowedAsLiteral:同page中的含义,要求container忽略和不对延迟EL语法进行解析。
  • trimDirectiveWhitespaces:同page中的含义,将directives周围的空格trim掉。
  • import:同page中的含义,可以引入java类,多个通过逗号分隔。
  • descriptiondisplay-namesmall-iconlarge-icon:通tld中的定义,略
  • body-content:是tld种的body-content的替代,但略有差异,有效值为empty, scriptless和tagdependent,没有jsp,即在tag文件中不允许使用scriptlets和expressions。缺省值为scriptless。
  • dynamic-attributes:同tld,表明是否允许动态属性,缺省为空,即不支持动态属性。如果需要设置,则设置为你需要获得所有动态变量的EL名字,这个EL是Map<String, String>类型,key是动态变量的名字,value是动态变量的值。
  • example:同tld,但在directive中进行有效设置比较困难。

此外,derective还有variable和attribute,同tld中的variable和attribute。variable有description, name-given, name-from-attribute,variable-class, declare, scope属性,同tld,此外还有alias,可以定义在一个本地变量名字来使用tag文件内的变量。attribute属性有 description, name, required, rtexprvalue, type, fragment, deferredValue,deferredValueType, deferredMethod和deferredMethodSignature,同tld。

一个简单的HTML模板Tag

我们写一个简单的tag文件,作为HTML的模板,方便填入HTML的title,编码等常规内容。新建/WEB-INF/tags/template/main.tag文件,如下:

<!-- body-content="scriptless":因为要支持HTML body内容,不使用empty,也不选择tagdependent(在通过query tag传递SQL statement时使用tagdependent)。-->
<!-- dynamic-attributes:支持动态属性(即EL),获取的参数名字为dynamicAttributes -->
<!-- 空格trim。在web.xml的<jsp-config>中我们定义了<trim-directive-whitespaces>true</trim-directive-whitespaces>,但是这个对tag文件无影响,因此我们需要在每个tag文件中手工进行定义 -->
<%@ tag body-content="scriptless" dynamic-attributes="dynamicAttributes" trimDirectiveWhitespaces="true" %>

<!-- 属性名字为htmlTitle;类型(String);该值允许runtime expression(rtexpvalue=true),即允许EL或者脚本表达式;必选(required=true) -->
<%@ attribute name="htmlTitle" type="java.lang.String" rtexprvalue="true" required="true" %>

<!-- 因为web.xml中jsp-conf设置不影响tag文件,如果我们在base.jspf中定义了共同的引入,需要再次手动引入,当然我们也可以通过%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %之类进行引入。 -->
<%@ include file="/WEB-INF/jsp/base.jspf" %>

<!DOCTYPE html>

<!-- 这里演示了动态属性加到现有html标签的例子:当中的c:out提供两个功能:显示value之前加了空格,即确保每个属性之前存在空格;禁止escapeXML,由于这些html的属性要真实有效,因此参数名不能escapeXML -->
<html<c:forEach items="${dynamicAttributes}" var="a">
         <c:out value=' ${a.key}="${fn:escapeXml(a.value)}"' escapeXml="false" />
     </c:forEach>>

     <head>
     	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
     	<!-- 设置title,删除前后的空格trim -->
        <title><c:out value="${fn:trim(htmlTitle)}" /></title>
    </head>

    <!-- jsp:doBody 仅在tag文件中使用,告诉容器计算JSP中本tag的body内容,并显示出来。可以加上var varReader属性和scope属性,将输入放入变量而不是显示出来 -->
    <body>
        <jsp:doBody />
    </body>
</html>

我们先补充一下base.jspf:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="template" tagdir="/WEB-INF/tags/template" %>

相应的在web.xml中:

  <jsp-config>
    <jsp-property-group>
      <url-pattern>*.jsp</url-pattern>
      <url-pattern>*.jspf</url-pattern>
      <page-encoding>UTF-8</page-encoding>
      <scripting-invalid>true</scripting-invalid>
      <include-prelude>/WEB-INF/jsp/base.jspf</include-prelude>
      <trim-directive-whitespaces>true</trim-directive-whitespaces>
      <default-content-type>text/html</default-content-type>
    </jsp-property-group>
  </jsp-config>

下面是一个是用该tag的jsp例子hello.jsp。例子中Hello, Template!就是<jsp:body/>的内容。

<template:main htmlTitle="Template Homepage">
    Hello, Template!
</template:main>

小例子:HTML模板

更为复杂的的tag文件:main.tag

我们将实现一个简单实用的HTML模板,最上是标题,左边是导航栏,右边是具体内容,如下图。


这里我们定义了两个fragment,分别对应HTML Title和导航栏。在官方文档http://docs.oracle.com/javaee/5/tutorial/doc/bnama.html#bnamr中fragment 如下定义。简单地说,fragment是可以包括html,jsp的一组脚本。

(optional) Whether this attribute is a fragment to be evaluated by the tag handler (true) or a normal attribute to be evaluated by the container before being passed to the tag handler.If this attribute is true:You do not specify the rtexprvalue attribute. The container fixes the rtexprvalue attribute at true.You do not specify the type attribute. The container fixes the type attribute at javax.servlet.jsp.tagext.JspFragment.Defaults to false.

将fragment的内容显示出来:

<jsp:invoke fragment="navigationContent" />

我们重写main.tag文件

<%@ tag body-content="scriptless" trimDirectiveWhitespaces="true" %>
<!-- 定义两个String参数:分别是HTML title和body Title -->
<%@ attribute name="htmlTitle" type="java.lang.String" rtexprvalue="true" required="true" %>
<%@ attribute name="bodyTitle" type="java.lang.String" rtexprvalue="true" required="true" %>

<!-- 定义两个fragment参数:分布是headConent(定义header的属性)和navigationContent(定义导航栏内容) -->
<%@ attribute name="headContent" fragment="true" required="false" %>
<%@ attribute name="navigationContent" fragment="true" required="true" %>
<%@ include file="/WEB-INF/jsp/base.jspf" %>
<!DOCTYPE html>
<html>
	<!-- header中,设置title以及header的其他属性headContent -->
	<head>
		<title>Customer Support :: <c:out value="${fn:trim(htmlTitle)}" /></title>
		<link rel="stylesheet" href="<c:url value="/resource/stylesheet/main.css" />" />
		<jsp:invoke fragment="headContent" />
	</head>
	<body>
		<h1>Multinational Widget Corporation</h1>
		<table border="0" id="bodyTable">
			<tbody>
				<tr>
					<td class="sidebarCell">
						<!-- 对应为导航栏,因此invoke响应的fragment -->
						<jsp:invoke fragment="navigationContent" />
					</td>
					<td class="contentCell">
						<!-- 对应为内容 -->
						<!-- 内容1:内容标题 -->
						<h2><c:out value="${fn:trim(bodyTitle)}" /></h2>
						<!-- 内容2:具体内容,将相应jsp body显示即可 -->
						<jsp:doBody/>
					</td>
				</tr>
			</tbody>
		</table>
	</body>
</html>

如何设置fragment:loggedOut.tag

对于登录界面,没有导航栏,实际上navigationCibtebt无实质内容,我们在main.tag的基础上提供loggedOut.tag。main有四个参数必选参数,在loggedOut.tag中,我们固化当中的两个fragment参数,另两个String参数则作为新tag的参数。我们将重点学习通过<jsp:attribute>对fragment进行设置。

<%@ tag body-content="scriptless" trimDirectiveWhitespaces="true" %>
<!-- 定义两个String参数,并作为传递到maintag中 -->
<%@ attribute name="htmlTitle" type="java.lang.String" rtexprvalue="true" required="true" %>
<%@ attribute name="bodyTitle" type="java.lang.String" rtexprvalue="true" required="true" %>
<%@ include file="/WEB-INF/jsp/base.jspf" %>
<template:main htmlTitle="${htmlTitle}" bodyTitle="${bodyTitle}">
	<!-- 设置headCotent参数。fragment都比较长,可能含有jsp代码,这时需要通过jsp:attribute来进行设置。 -->
	<jsp:attribute name="headContent">
		<link rel="stylesheet" href="<c:url value="/resource/stylesheet/login.css" />" />		
	</jsp:attribute>
	<!-- 设置navigationContent参数,由于没有实际的边框栏,因此该参数无实际值 -->
	<jsp:attribute name="navigationContent" />
	<!-- tag的body作为具体内容,需要传递到main tag中的body。 -->
	<jsp:body>
		<jsp:doBody />
	</jsp:body>
</template:main>

具体化导航栏:basic.tag

main.tag是通用的模板,我们可以具体化到实际的项目中,包括:

  1. 将headContent映射为extraHeadContent。
  2. 将navigationContent具体化为通用的导航链接加上extraNavigationContent。

在这个例子中,我们将展现如果在fragment中使用其他fragment。

<%@ tag body-content="scriptless" trimDirectiveWhitespaces="true" %>
<!-- 仍使用四个参数 -->
<%@ attribute name="htmlTitle" type="java.lang.String" rtexprvalue="true" required="true" %>
<%@ attribute name="bodyTitle" type="java.lang.String" rtexprvalue="true" required="true" %>
<%@ attribute name="extraHeadContent" fragment="true" required="false" %>
<%@ attribute name="extraNavigationContent" fragment="true" required="false" %>
<%@ include file="/WEB-INF/jsp/base.jspf" %>
<template:main htmlTitle="${htmlTitle}" bodyTitle="${bodyTitle}">
	<!-- 将headContent映射为extraHeadContent -->
	<jsp:attribute name="headContent">
		<jsp:invoke fragment="extraHeadContent" />
	</jsp:attribute>
	<!-- 将navigationContent具体化为通用的导航链接加上extraNavigationContent。-->
	<jsp:attribute name="navigationContent">
		<a href="<c:url value="/tickets" />">List Tickets</a><br />
		<a href="<c:url value="/tickets">
		  			<c:param name="action" value="create" />
				 </c:url>">Create a Ticket</a><br />
		<a href="<c:url value="/sessions" />">List Sessions</a><br />
		<a href="<c:url value="/login?logout" />">Log Out</a><br />
		<jsp:invoke fragment="extraNavigationContent" /> 
	</jsp:attribute>
	<jsp:body>
		<jsp:doBody />
	</jsp:body>
</template:main>

使用tag

用户未登录或者登录失败,重定向至login.jsp,如果登录失败,给出失败提示。

<%--@elvariable id="loginFailed" type="java.lang.Boolean"--%>
<%@ taglib prefix="template" tagdir="/WEB-INF/tags/template" %>
<template:loggedOut htmlTitle="Log In" bodyTitle="Log In">
	You must log in to access the customer support site.<br /><br />
	<c:if test="${loginFailed}">
		<b>The username or password you entered are not correct. Please try again.</b>
	</c:if>
		
	<form method="POST" action="<c:url value="/login" />">
		Username<br/>
		<input type="text" name="username" /><br/><br/>
		Password<br/>
		<input type="password" name="password" /><br/><br/>
		<input type="submit" value="Log in" />
	</form>
</body>
</template:loggedOut>

在LoginServlet.jsp中:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	... 判断是否用户登录成功  ...
	if(登录失败){
		request.setAttribute("loginFailed", true);
		request.getRequestDispatcher("/WEB-INF/jsp/view/login.jsp").forward(request, response);		
	}else{ //登录成功
		session.setAttribute("username", username);
		request.changeSessionId(); //这里注意一下,基于安全,修改了sessionId。
		response.sendRedirect(......);
	}
}

我们再看一个基于basic tag的例子,ticketForm.jsp

<template:basic htmlTitle="Create a Ticket" bodyTitle="Create a Ticket">
        <form method="POST" action="tickets" enctype="multipart/form-data">
            <input type="hidden" name="action" value="create"/>
            Subject<br/>
            <input type="text" name="subject"><br/><br/>
            Body<br/>
            <textarea name="body" rows="5" cols="30"></textarea><br/><br/>
            <b>Attachments</b><br/>
            <input type="file" name="file1"/><br/><br/>
            <input type="submit" value="Submit"/>
        </form>
</template:basic>


相关链接: 我的Professional Java for Web Applications相关文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值