- JavaWeb從入門到精通(視頻實戰版)
- 常倬林等編著
- 3064字
- 2018-12-31 19:36:05
6.3 使用HTML標簽
與通用標簽不同,HTML標簽不過多提供控制結構或邏輯。而是著重于如何使用action類、value stack或者Data Tags中的數據,并且在HTML中呈現出來。如果說普通標簽只是簡單地做些輸出結果(如果有內容),HTML標簽的輸出則是因模板(template)而異,它們常常組合在一起作為一個主題(theme),做實際的渲染輸出HTML的工作。關于模板和主題將在以后的章節詳細介紹。
Struts2竭力支持開發者所偏愛的技術,這也是Struts2沒有綁定于某一種特定的模板語言的原因。Struts2支持幾乎所有應用廣泛的模板語言甚至還為新語言提供了接口。默認情況下,幾乎每一個標簽都支持JSP、Velocity和FreeMarker。
注意
Struts2中默認的模板語言是FreeMarker,而不像其前身WebWork中默認的是Velocity。關于兩者的優劣有很多評論,一般的說法是FreeMarker提供的錯誤信息更多,更加容易調試。
6.3.1 模板和主題
Struts提供的html標簽的核心是主題(theme)和模板(template)。首先明確一下3個概念:
1)tag(標簽):小段代碼,在JSP、FreeMarker或者Velocity里執行。
2)template(模板):一個文件,通常使用JSP或者FreeMarker編寫,能被特定的標簽(HTML tags)輸出。
3)theme(主題):一系列templates打包在一起,提供通用的功能。
Struts2提供了4種主題可以選擇:
1)simple主題:一個最小的主題,包含了最少的輔助功能。使用simple主題的缺點就是它不支持其他主題那么多的屬性。例如label屬性在simple主題里沒有任何用處。類似地,simple主題提供的功能也遠遠少于xhtml和ajax主題,自動顯示錯誤信息就不被支持。
2)xhtml主題:使用了通用的HTML實踐的默認主題。
3)css_xhtml主題:使用嚴格的CSS布局對xhtml主題重新實現。
4)ajax主題:一個基于xhtml主題的主題,提供了高級AJAX特性。
關于主題有兩項配置,它們定義在struts.properties文件中,如表6.18所示。
表6.18 主題的配置

注意
使用simple主題的缺點就是它不支持其他主題那么多的屬性。例如,label屬性在simple主題里沒有任何用處。類似地,simple主題提供的功能也遠遠少于xhtml和ajax主題提供的:自動顯示錯誤信息就不被支持。
6.3.2 通用屬性
每個UI標簽都有一些共有的通用屬性。除了那些處理label的屬性,其他屬性都可以在simple和xhtml主題上使用。表6.19列出了UI標簽所有的通用屬性。
表6.19 UI標簽所有的通用屬性

如果沒有設定ID屬性,那么Struts2表單標簽會自動設置一個ID。form標簽的ID就是其action的名字,如“updatePerson”。對于Form內的表單元素ID被設定為所在form的ID+"_"+元素的name屬性,如“updatePerson_username”。
除了表6.19中的通用屬性之外,所有的UI標簽也支持通常設置的JavaScript事件。這允許簡單的JavaScript集成并讓表單更具有交互性,見表6.20。
表6.20 UI標簽支持的JavaScript事件

所有的UI標簽還有一項特性很有趣,也很有用,那就是“name-value聯動”。通常在構建一個表單時輸入數據的字段也是從它獲取數據的字段,并會把它顯示在表單中(通過value屬性賦值)。常見的就是在修改屬性的表單中,如下代碼:
<s:form action="updateUser"> <s:textfield name="user.firstName" value="%{user.firstName}"/> </s:form>
Struts2提供一個簡單的方式,可以自動設置value的值,無須以顯式的方式給value賦值了。如下代碼所示:
<s:form action="updateUser"> <s:textfield name="user.firstName“/> </s:form>
當然,有時不需要默認的值在表單中作為這個字段的值,可以給value屬性賦其他值。
6.3.3 表單標簽介紹
在HTML中的表單元素在Struts2中都提供了對應的標簽來支持,見表6.21所示。
表6.21 表單標簽成員列表

表單標簽成員眾多,但使用方法都比較類似。本書只介紹幾種典型的標簽。
1. form標簽
form標簽是所有UI標簽中獨一無二的,因為它擔當了容器的角色。它有一個起點(<s:form>)和一個終點(</s:form>)。在simple主題里,它只輸出html的form元素,而在xhtml主題中,它除了form元素之外還輸出周圍的表格。form標簽的屬性如表6.22所示。
表6.22 form標簽屬性表

要注意最重要的兩個屬性是action和namespace。它們組合起來,用來把form鏈接到一個特殊的action。例如,如下代碼會被提交到/secure/updateProfile.action:
<s:form action="updateProfile" namespace="/secure">
2. textfield標簽
textfield標簽是最常用的一個UI標簽,輸出一個text類型的HTML input元素,在前面的章節中已經多次使用。textfield標簽的屬性如表6.23所示。
表6.23 textfield標簽屬性表

3. textarea標簽
textarea標簽用來填寫比textfield標簽更大數量的文本(包括換行符),不像textfield和password標簽那樣輸出HTML<input>標記,這個標簽輸出HTML<textarea>標記。textarea標簽的參數如表6.24所示。
表6.24 textarea標簽參數表

4. checkbox標簽
不像其他標簽,checkbox標簽不把字段的value當成字符串類型。也就是說,復選框必須有一個字段為布爾量來求值或者能被轉化為布爾類型。
<s:checkbox label=" BOX" name=user.address.poBox fieldValue="true'>
注意
復選框和其他的標簽有點不同。HTML的規范方式要求只有當復選框被選中的時候才會提交對應的值。如果復選框沒有被選中,那么提交的參數中就根本不包含這個項目。因此接受參數的model設計時,應該將默認值定為false,很可能是model此字段沒有被賦值。這樣做可以保證表單提交時不論復選框狀態為何,都會得到期望的值。
5. select標簽
創建一個HTML Select列表組件。select標簽的參數如表6.25所示。
表6.25 select標簽參數表

最重要的屬性是list,它告訴標簽要選擇的選項從哪里來。在最簡單的表單里,列表呈現給用戶,選擇的值會被提交到表單并被映射到指定的字段,也是與名字對應的屬性。如果一個值被預先設置,那么列表里這個字段具有相同值的選項就會自動被選中。
<s:select label="State" name="user.address.state" list="{'江西','湖北'}">
在這個例子中,表達式user.address.state的值會被設置為江西或者湖北。如果填入的值是這兩個值中的一個,select標簽就會設置表單。輸出的HTML可能是
<select> <option>'江西'<option> <option>'湖北'<option> </select>
很多時候可能會希望option的顯示值和實際值是不同的。比如:
<select> <option value=1>'江西'<option> <option value=2>'湖北'<option> </select>
這需要給select標簽設置listKey和listValue屬性就可以。listKey和listValue特性是通過迭代這個列表并在循環中把對象放到stack頂部的方式進行的。這也是iterator標簽使用的相同的方式。如果使用的是一個map作為select標簽進行迭代的列表,對象會通過Map.Entry進行迭代,就像iterator標簽一樣。因為Map.Entry提供了getKey()和getValue()方法,這些通常被用來作為listKey和listValue的值。使用基于Map的方法來重新編寫state下拉框:
<s:select label="State" name="user.address.state" list="#{1:'江西', 2:'湖北'}">
6.3.4 非表單標簽
非表單標簽與表單標簽相反,是為輸出一些表單以外的HTML元素設計的,主要用于顯示而非提交參數。它們也會帶來很多方便,如table自動排序等。非表單標簽成員如表6.26所示。
表6.26 非表單標簽成員列表

需要解釋一下的是component標簽,使用特定的模板輸出一個自定義的UI widget(組件)。附加的對象可以通過param標簽傳遞給模板,設置的對象可以在模板里面通過 $parameters.paramname獲取。如在component(組件)內部,它們可以通過$parameters.get('key1') 和 $parameters.get('key2') 的方式被訪問,也允許通過$parameters.key1和$parameters.key2來引用它們。
component標簽提供了一種建立自定義UI標簽的方式。例如,假設想要一個包含3項的復選框來表示on/off/none,可以使用HTML來編寫一個,用JavaScript和一些定制的圖像來代表3個狀態。
6.3.5 標簽實例
首先看一下需要表現的頁面,是一個用戶信息修改提交頁面,如圖6.11所示。

圖6.11 標簽舉例界面圖
如果用普通JSP表現,如實例6-14所示,在firstname字段后面有關于錯誤信息的處理,如果有錯誤信息,就在這個字段后面打印錯誤日志。
【實例6-14】標簽實例:updateProfile.jsp
01 <%@ page 02 import="org.hibernate.auction.model.User,org.hibernate.auction.model.Address, java.util.Map,java.util.Collections"%> 03 <% 04 User user = (User) request.getAttribute("user"); 05 Map fieldErrors = (Map) request.getAttribute("fieldErrors"); 06 if (fieldErrors == null) { 07 fieldErrors = Collections.EMPTY_MAP; 08 } 09 %> 10 <html> 11 <head> 12 <title><s:text name="title" />UpdateProfile</title> 13 </head> 14 <body> 15 <form action="updateProfile.action" method="post"> 16 <table> 17 <% if (fieldErrors.containsKey("user.firstname")) {%> 18 <tr> 19 <td align="center" valign="top" colspan="2"> 2 0 <span class="errorMessage"> <%= fieldErrors.get("user.firstname")%> 21 </span> 22 </td> 23 </tr> 24 <% }%> 25 <tr> 26 <td align="right"> 27 <label> 28 First name£o 29 </label> 30 </td> 31 <td> 32 <input type="text" name="user.firstname" 33 value="<%= user.getFirstname() %>" /> 34 </td> 35 </tr> 36 <tr> 37 <td align="right"> 38 <label> 39 Last name£o 40 </label> 41 </td> 42 <td> 43 <input type="text" name="user.lastname" 44 value="<%= user.getLastname() %>" /> 45 </td> 46 </tr> 47 <tr> 48 <td align="right"> 49 <label> 50 Email£o 51 </label> 52 </td> 53 <td> 54 <input type="text" name="user.email" 55 value="<%= user.getEmail() %>" /> 56 </td> 57 </tr> 58 <tr> 59 <td align="right"> 60 <label> 61 Gender£o 62 </label> 63 </td> 64 <td> 6 5 <input type="radio" name="user.gender" value="0" id="user.gender0" 6 6 <% if (user.getGender() == 0) { %> checked="checked" <% } %> /> 67 <label for="user.gender0"> 68 Male 69 </label> 7 0 <input type="radio" name="user.gender" value="1" id="user.gender1" 7 1 <% if (user.getGender() == 1) { %> checked="checked" <% } %> /> 72 <label for="user.gender1"> 73 Female 74 </label> 75 </td> 76 </tr> 77 <% 78 Address address = user.getAddress(); 79 boolean nullAddress = address == null; 80 %> 81 <tr> 82 <td align="right"> 83 <label> 84 Street Address: 85 </label> 86 </td> 87 <td> 88 <input type="text" name="user.address.street" 89 value="<%= !nullAddress ? address.getStreet() £o ""%>" /> 90 </td> 91 </tr> 92 <tr> 93 <td align="right"> 94 <label> 95 Zip Code£o 96 </label> 97 </td> 98 <td> 99 <input type="text" name="user.address.zipcode" 100 value="<%= !nullAddress ?address.getZipcode() £o ""%>" /> 101 </td> 102 </tr> 103 <tr> 104 <td align="right"> 105 <label> 106 City£o 107 </label> 108 </td> 109 <td> 110 <input type="text" name="user.address.city" 111 value="<%= !nullAddress ?address.getCity() £o ""%>" /> 112 </td> 113 </tr> 114 <tr> 115 <td align="right"> 116 <label> 117 State£o 118 </label> 119 </td> 120 <td> 121 <select name="user.address.state"> 122 <option value="Californa" 123 <% if (!nullAddress && 124 "California".equals(address.getState())) { %> 125 selected="selected" <% } %>> 126 Californa 127 </option> 128 <option value="Oregon" 129 <% if (!nullAddress && 130 "Oregon".equals(address.getState())) { %> 131 selected="selected" <% } %>> 132 Oregon 133 </option> 134 </select> 135 </td> 136 </tr> 137 <tr> 138 <td align="right"> 139 <label> 140 Country£o 141 </label> 142 </td> 143 <td> 144 <select name="user.address.country"> 145 <option value="USA" 146 <% if (!nullAddress && "USA".equals(address.getCountry())) { %> 147 selected="selected" <% } %>> 148 USA 149 </option> 150 <option value="Canada" 151 <% if (!nullAddress &&"Canada".equals(address.getCountry())) { %> 152 selected="selected" <% } %>> 153 Canada 154 </option> 155 <option value="Mexico" 156 <% if (!nullAddress &&"Mexico".equals(address.getCountry())) { %> 157 selected="selected" <% } %>> 158 Mexico 159 </option> 160 <option value="Other" 161 <% if (!nullAddress &&"Other".equals(address.getCountry())) { %> 162 selected="selected" <% } %>> 163 Other 164 </option> 165 </select> 166 </td> 167 </tr> 168 <tr> 169 <td colspan="2"> 170 <table> 171 <tr> 172 <td valign="middle"> 173 <input type="checkbox" name="user.address.poBox" value="true" 174 <% if (!nullAddress && address.isPoBox()) { %> 175 checked="checked" <% } %> /> 176 </td> 177 <td valign="middle" style="width:100%"> 178 <label class="checkboxLabel"> 179 P.O. Box 180 </label> 181 </td> 182 </tr> 183 </table> 184 </td> 185 </tr> 186 <tr> 187 <td colspan="2"> 188 <div align="'right'"> 189 <input value="Update Profile" type="submit" /> 190 </div> 191 </td> 192 </tr> 193 </table> 194 </form> 195 </body> 196 </html>
【代碼剖析】在上述代碼中,如果改為由Struts2的標簽實現,那么該代碼里190多行的代碼只剩下30行就可以完成,Strus2的標簽威力可見一斑。
通過使用Struts2標簽不僅輸入與Java值對象的關系是自動完成的,而且控件的布局(在xhtml主題中對form及其中的控件都使用table做了自動布局等渲染工作,在sample主題中不包含自動布局功能。請參考6.5.3節中的介紹)、錯誤信息的處理都是由標簽完成,用戶無須再干預,具體內容如實例6-15所示。
【實例6-15】標簽實例:updateProfile1.jsp
01 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 02 <%@ page import="java.util.Map,java.util.Collections"%> 03 <%@ taglib prefix="s" uri="/struts-tags"%> 04 <html> 05 <head> 06 <title>UpdateProfile</title> 07 <s:head /> 08 </head> 09 <body> 10 <!--使用標簽實現JSP--> 11 <s:form action="updateProfile" method="post"> 12 <s:textfield label="First name" name="user.firstname" 13 cssStyle="float:left; color:red" /> 14 <s:textfield label="Last name" name="user.lastname" /> 15 <s:textfield label="Email" name="user.email" /> 16 <s:radio label="Gender" name="user.gender" 17 list="#{0 : 'Male', 1 : 'Female'}" /> 18 <s:textfield label="Street" name="user.address.street" /> 19 <s:textfield label="Zip Code" name="user.address.zipcode" /> 20 <s:textfield label="City" name="user.address.city" /> 21 <s:select label="State" name="user.address.state" 22 list="{'Californa', 'Oregon'}" /> 23 <s:select label="Country" name="user.address.country" 24 list="{'USA', 'Canada', 'Mexico', 'Other'}" /> 25 <s:checkbox label="P.O. Box" name="user.address.poBox" 26 fieldValue="true" /> 27 <s:submit value="Update Profile" /> 28 </s:form> 29 <s:set name="user" value="user" scope="request" /> 30 <s:set name="fieldErrors" value="fieldErrors" scope="request" /> 31 </body> 32 </html>
【代碼剖析】由于Struts2會自動增加控件的樣式所有,如果想自己定義空間的樣式不能再使用class屬性,要使用Struts2單獨提供的cssClass、cssStyle屬性。這兩個屬性在通用屬性中已經有過介紹,是所有控件都具有的屬性。

圖6.12 OGNL對象范圍
- Mastering Adobe Captivate 2017(Fourth Edition)
- 深入淺出Java虛擬機:JVM原理與實戰
- 差分進化算法及其高維多目標優化應用
- Kali Linux Wireless Penetration Testing Beginner's Guide(Third Edition)
- EPLAN實戰設計
- GameMaker Programming By Example
- ANSYS Fluent 二次開發指南
- Go語言編程
- SQL Server 2016 從入門到實戰(視頻教學版)
- ActionScript 3.0從入門到精通(視頻實戰版)
- Node.js實戰:分布式系統中的后端服務開發
- Android初級應用開發
- Learning TypeScript
- HTML5 and CSS3:Building Responsive Websites
- 三步學Python