(八)Struts标签基础(一)
一、Struts标签分类
二、标签的使用
2.1 标签的主题
- 主题的设置与struts.xml中的常量<constant name="struts.ui.theme" value="xhtml" /> 决定。
- 每个主题都会对一些标签产生作用,而这些作用被定义在一些文件文件里,比如
checkbox.ftl这个文件定义的是对checkbox标签产生作用的语法。
- 主题共有以下几种(版本为struts2-core-2.3.14.jar):
路径为:struts2-core-2.3.14.jar包里的
2.1.1 默认主题即template.xhtml , 会给标签自动添加一些属性或者子标签,可能打乱我们设计好的布局
<body> <pre> <h3>默认主题</h3>
<table border="1"> <tr> <td>用户名</td> <td> <s:textfield></s:textfield> </td> </tr>
</table> </pre> </body>
- 我们原本的设计是这个表格一共一行且只有两个空格, 但是结果为:
- 出现这种情况是因为,template.xhtml这个主题会自动帮标签添加一些属性或子标签,查看源代码可知产生了td和tr子标签:
2.1.2 simple主题(最常用),额外添加的标签很少,不会对我们的布局产生大的影响。
- 在struts.xml中设置常量<constant name="struts.ui.theme" value="simple" />
index.jsp
<body> <pre> <h3>默认主题</h3> <table border="1"> <tr> <td>用户名</td> <td> <s:textfield></s:textfield> </td> </tr> </table> </pre> </body>
查看源码:
- 对比我们的代码,文本框只添加了name属性
- 注意:在编写页面标签的时候,最好先把主题设置为simple主题,否则在布局的时候会很麻烦。
2.1.3 定制主题
- 步骤:
- 在src目录下新建一个文件夹,叫template (template名不能改成别的)
- 在template中在新建一个文件夹,名字自定义。
- 在自定义的文件夹中,将要修改的控制的ftl文件拷贝,进行修改。比如我们定制的主题中有对文本输入标签进行设定,那么我们就可以到simple这个主题里找到text.ftl这个文件(注意:text.ftl这个文件主题系统会自动作用在文本框里,如果换成别的名字则无法起作用),然后拷贝到我们自定义的文件夹里,再对text.ftl这个文件进行修改。
- 把text.ftl中需要添加的文件添加进来,text.ftl需要以下四个文件,然后在simple主题中找到这四个文件然后拷贝到自定义文件夹里即可。
-
- 修改text.ftl
红框内的内容是我们添加的css属性。
-
- 最后在标签中应用此主题,<s:textfield theme="self"></s:textfield> //self为tempalte包的主题包名,因为这里我们只定义了一个标签的主题,如果我们在在self自定义主题包里对大量标签进行了主题设定,那么我不必每个标签都使用theme属性来引入主题,只需要struts.xml中设置常量<constant name="struts.ui.theme" value="self" /> 即可。
结果:
可知标签主题已经变成我们自定义的了。
2.2 表单标签
2.2.1 为什么要使用表单标签?(struts表单标签和input标签的区别)
- struts表单标签可以与Action中的属性进行绑定,可以实现属性值到控件的Value值回填的操作。
- 示例:
- index.jsp:
<body> <a href="<%=path%>/tag/form">表单标签</a> </body>
- struts.xml
<struts> <constant name="struts.i18n.encoding" value="UTF-8"></constant> <constant name="struts.multipart.maxSize" value="209715200"></constant> <constant name="struts.action.extension" value="action,,"></constant> <constant name="struts.enable.DynamicMethodInvocation" value="true" /> <constant name="struts.devMode" value="true" /> <constant name="struts.i18n.reload" value="true"></constant> <constant name="struts.ui.theme" value="simple" /> <constant name="struts.configuration.xml.reload" value="true"></constant> <constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant> <constant name="struts.handle.exception" value="true"></constant> <package name="default" namespace="/tag" extends="struts-default"> <action name="form" class="tag.FromAction"> <result name="formjsp">/tag/form.jsp</result> </action> </package> </struts>
-
FromAction .java
package tag; public class FromAction { private String textName; public String getTextName() { return textName; } public void setTextName(String textName) { this.textName = textName; } public String execute(){ this.textName="Action对成员变量textNam赋值后会自动回填到表单中"; return "formjsp"; } }
- form.jsp
<body> <pre> <s:textfield name="textName"></s:textfield> </pre> </body>
结果:
- 由结果可知,我们在Action中设置了一个成员变量textName,而这个成员变量名和<s:textfield name="textName"></s:textfield>是一样的,所以当我们在Action中对textName进行赋值的时候,struts会自动把值回填到<s:textfield name="textName"></s:textfield>标签里。 而如果在input标签中要实现数据的回填,则要在servlet中把值存放在作用域中,然后在jsp页面里用el表达式显示出来才行。
- input标签实现数据回填:
把值存放在作用域中 :request.setAttribute("属性", "input标签实现数据回填");
在jsp页面中把值取出来并显示: <input type="text" value="${requestScope.属性}"/>或者 <input type="text" value="<s:property value="#request.属性"/>"/>(request.setAttribute会吧属性放在广义值栈中的非狭义值栈的位置,所以需要用#来取值)。
2.1.2 struts表单标签的分类
-
- 文本框标签:<s:textfield name="textName"></s:textfield> name属性用来与Action中的成员变量绑定,请查看上例。
-
- 文本域标签:<s:textarea name="remark"></s:textarea>
-
- 密码框标签:<s:password name="passWd"></s:password>
- 静态单选框_1 :
<tr> <td>静态单选框:</td> <td> <s:radio list="{'男','女'}" name="sex_1"></s:radio></td> </tr>
静态单选框中的list属性是必填的,list里的{}是OGNL表达式,意思是定义一个集合,本题中{'男','女'}意思是定义一个集合且这个集合中有两个string值。且list中集合有几个值那么就有几个单选按钮。如下图,每个单选按钮的值对应着集合中的值,如右下图。
-
- 静态单选框_2 :单选框的值由我们来设置,而不是静态单选框_1 由集合的值来定义。这里用的是OGNL表达式中的“#{}” 表示定义一个MAP集合。
<tr> <td>静态单选框_2:</td> <td><s:radio list="#{1:'男',0:'女'}" name="sex_1" ></s:radio></td> </tr>
map集合中的1代表按钮value=“1”,表示如果用户选择的是“男”这个按钮,提交到后台的数据是“1” ,另一个按钮也是如此,查看源码:
- 单选框使用:
在JSP页面中创建单选按钮radio的方法:
<s:radio list="#{'1':'先生','0':'女士'}" name="gender" value="1"/>
其中list中的键值对表示所有的选项,value表示设置的默认值,如果这个默认值是从后台传过来的,可以这样设置:
<s:radio list="#{'1':'先生','0':'女士'}" name="gender" value="gender.id"/>
当list属性为Action传过来的Map时 可以自动显示为key-value形式
<s:radio list="%{map}" name="gender" value="gender.id "/>
当list属性为Action传过来的List<Gender>时 需要添加 listKey listValue属性 listKey对应提交到数据库中的值 listValue对应显示的文本
<s:radio list="%{list}" name="gender" value="gender.id" listKey="id" listValue="genderText""/>
-
- 动态单选框_1: 按钮value是从数据库或者其他地方获取的。
- 示例:从数据库中读取值。
A、 设计数据库并插入数据:
B、 加载数据库驱动jar包,使用数据库开源工具包(ommons-dbutils-1.3.jar),可以帮助我们快速操作数据库,编写操作数据库的工具类。
B1. 编写工具类,获取数据库的连接Connection conn,DBUtil.java :
package DBUtil; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBUtil { private static final String DRIVER="com.mysql.jdbc.Driver"; private static final String USER="root"; private static final String PASSWD=""; private static final String URL="jdbc:mysql://127.0.0.1:3306/user?useUnicode=true&characterEncoding=UTF-8"; static{ try { Class.forName(DRIVER); } catch (Exception e) { throw new RuntimeException("无法加载驱动包"); } } public static Connection getConn(){ Connection conn=null; try { conn= DriverManager.getConnection(URL,USER,PASSWD); } catch (SQLException e) { e.printStackTrace(); } return conn; } }
B2. 使用数据库开源工具包(ommons-dbutils-1.3.jar),这个包的类提供了快速增删改查方法的实现。FormAction.java
package tag; import java.util.List; import org.apache.commons.dbutils.DbUtils; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.ResultSetHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import com.opensymphony.xwork2.ActionContext; import DBUtil.DBUtil; import actionUtil.BaseAction; import bean.UserBean; public class FromAction extends BaseAction{ /** * QueryRunner类为数据库开源工具包(ommons-dbutils-1.3.jar)封装的类, * 这个类实现了快速对数据库操作的方法。 * 步骤: * 确认sql语句确定相应的方法,本题中select * 查询结果应该是一个集合。所以应该用QueryRunner的query()方法, * 而query()方法里的三个参数为“Connection conn,String sql,ResultSetHandler<T> t”其中ResultSetHandler * 是一个接口,我们往query()方法里填的参数不可能是接口而只能是其实现类,其中ResultSetHandler<T>的实现类BeanListHandler<T>类 * 是本题中需要的类,把这个类的对象加到query()方法里,这个方法将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里,也就是说我们 * 此时还需要创建一个javaBean类,这个类的成员属性(要有getset方法)要跟数据库中的列名一致。 * */ public String execute() throws Exception{ ActionContext actionContext=ActionContext.getContext(); String sql="select * from user order by userId"; QueryRunner query=new QueryRunner(); List<UserBean> list=query.query(DBUtil.getConn(), sql, new BeanListHandler<UserBean>(UserBean.class)); actionContext.put("list", list); return "formjsp"; } }
- 补充:ResultSetHandler的各个实现类:
ArrayHandler:把结果集中的第一行数据转成对象数组。
ArrayListHandler:把结果集中的每一行数据都转成一个对象数组,再存放到List中。
BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。//重点
MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。//重点
MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List
ColumnListHandler:将结果集中某一列的数据存放到List中。
KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里(List<Map>),再把这些map再存到一个map里,其key为指定的列。
ScalarHandler:将结果集第一行的某一列放到某个对象中。//重点
B3. 在jsp页面把值取出来 from.jsp
<tr> <td>静态单选框_2:</td> <td><s:radio list="#list" name="sex_1" listKey="userId" listValue="userName"></s:radio></td> </tr>
- list="#list" 表示往非广义值栈里取值为list的属性值(此时list存放的是javaBean对象的地址,所以取出来的也是地址),listValue="userName" 表示标签的属性value为javaBean对象的userName属性,
listKey="userId" 表单当用户选中某个选项的时候返回到后台的值。
结果:
参考刚才数据库里的数据
- 动态单选框_2: 按钮value是从数据库或者其他地方获取的。(与动态单选框_1类似)
- 在上例中,把DBUtil.java 文件修改如下(只是把上例的list转为Map):
package tag; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.dbutils.DbUtils; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.ResultSetHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import com.opensymphony.xwork2.ActionContext; import DBUtil.DBUtil; import actionUtil.BaseAction; import bean.UserBean; public class FromAction extends BaseAction{ /** * QueryRunner类为数据库开源工具包(ommons-dbutils-1.3.jar)封装的类, * 这个类实现了快速对数据库操作的方法。 * 步骤: * 确认sql语句确定相应的方法,本题中select * 查询结果应该是一个集合。所以应该用QueryRunner的query()方法, * 而query()方法里的三个参数为“Connection conn,String sql,ResultSetHandler<T> t”其中ResultSetHandler * 是一个接口,我们往query()方法里填的参数不可能是接口而只能是其实现类,其中ResultSetHandler<T>的实现类BeanListHandler<T>类 * 是本题中需要的类,把这个类的对象加到query()方法里,这个方法将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里,也就是说我们 * 此时还需要创建一个javaBean类,这个类的成员属性(要有getset方法)要跟数据库中的列名一致。 * */ public String execute() throws Exception{ ActionContext actionContext=ActionContext.getContext(); String sql="select * from user order by userId"; QueryRunner query=new QueryRunner(); List<UserBean> list=query.query(DBUtil.getConn(), sql, new BeanListHandler<UserBean>(UserBean.class)); Map<String,String> userMap=new HashMap<String, String>(); if(list!=null){ /** * 把List转换为Map */ for(UserBean user:list){ String userName=user.getUserName(); String userId=user.getUserId(); userMap.put(userId, userName); } } actionContext.put("userMap", userMap); return "formjsp"; } }
form.jsp:
- 在上例中,把DBUtil.java 文件修改如下(只是把上例的list转为Map):
<tr> <td>静态单选框_2:</td> <td><s:radio list="#userMap" name="sex_1" ></s:radio></td> </tr>
- 此时就不用添加listValue和listKey属性了,因为Map就是键值对。
-
- 下拉框标签:
- 下拉框也有动态下拉框如果需要的话,可以参考动态单选框_1/动态单选框_2,但是下拉框一般是固定的值,没必要动态获取。
- 下拉框标签:
<tr> <td>下拉框</td> <td><s:select list="{'福建','山东','河南'}" name="privince" headerKey="none" headerValue="---请选择---"></s:select></td> </tr>
-
- 下拉框分组:
<tr> <td>下拉框分组</td> <td> <s:select list="{'福建','山东','河南'}" name="privince" headerKey="none" headerValue="---请选择---"> <s:optgroup list="#{1:'泉州',2:'莆田',3:'福州'}" label="福建" ></s:optgroup> </s:select> </td> </tr>
-
<s:optgroup >标签只能放在<s:select>标签里,且list只能用"#{}"这个OGNL表达式也就是一定要用Map,否则错误。
label标签不可选定。
-
-
- 复选框标签和复选框分组标签:
-
本例的文件与动态单选框_1中的文件相同,只有最后的form.jsp页面改成下面
<tr> <td>复选框:</td> <td><s:checkbox name="is_check" value="true"></s:checkbox></td> </tr> <tr> <td>复选框分组:</td> <td><s:checkboxlist list="#userMap" name="group"></s:checkboxlist></td> </tr>
结果:
- 其中list="userMap"是数据库的数据封装为Map对象,而且<s:checkboxlist>中的list和name属性一定要写,name如果没写ftl文件会报name属性未定义这个错误。
-
<s:checkboxlist>中的list如果是List集合,那么就要加listValue和listKey属性。
隐藏域和文件域名标签
<tr> <td>隐藏域</td> <td><s:hidden name="userId"></s:hidden ></td> </tr> <tr> <td>文本域</td> <td><s:file name="userface"></s:file ></td> </tr>
结果:
-
- 表单标签:
<s:form name="" id="" action="" method=""></form>
-
- 按钮标签:
- struts中没有普通按钮标签。
-
- 上下下拉框(使用这个标签要在<head>标签里定义<s:head />标签,这个标签用于引入上下下拉框等标签所需的脚本文件)
<tr>
<td>上下下拉框</td>
<td><s:updownselect list="{'1','2','3'}" name="text" cssStyle="width:60px"
allowMoveDown="true" allowMoveUp="true" allowSelectAll="true" moveDownLabel="向下移动" moveUpLabel="向上移动" selectAllLabel="全选"
></s:updownselect></td>
</tr>
- 用法类似动态单选框_1和动态单选框_2
-
- 选项传输下拉框(1. 使用这个标签要在<head>标签里定义<s:head />标签,这个标签用于引入上下下拉框等标签所需的脚本文件 2. 使用这个标签的时候不能用submit按钮提交,否则后台取值的时候会全部取到而不是只取用户选定的,只能通过定义普通按钮然后这个按钮触发一个javaScript脚本,在脚本里定义一个submit()方法提交)
-
- 组合框标签(1.使用这个标签要在<head>标签里定义<s:head />标签,这个标签用于引入上下下拉框等标签所需的脚本文件 2. 使用这个标签的时候不能用submit按钮提交,否则后台取值的时候会全部取到而不是只取用户选定的,只能通过定义普通按钮然后这个按钮触发一个javaScript脚本,在脚本里定义一个submit()方法提交)