将controller放入中Tile最简单的方法是在<insert>和<definition>中指定,可以是本地URL或一个类。在Jsp页面显示前controller被调用。controller使用共享的Tile内容填充Jsp页面,所以它可以读取、修改和添加Tile属性。controller一般用来做这么几件事情:model的数据被传递到view前进行处理;属性传递到view前对其进行修改或添加。 你可以使用当前web应用程序的Structs Action URL来做为controller,你的action可以扩展org.apache.struts.action.TilesAction,来替代Structs Action类。TilesAction继承原来的execute()方法。还提供了一个新的execute()方法,这个方法多了"tileContext"参数,当你需要处理Tile的属性是很有用。
<tiles:insert page="layout.jsp" controllerUrl="myAssociatedAction.do” > <tiles:put name="title" value="Test controller set in insert" /> <tiles:put name="header" value="header.jsp" /> <tiles:put name="body" value="body.jsp" /> </tiles:insert> Structs Action中的描述: <action path="/myAssociatedAction" type="org.apache.struts.example.tiles.MyAssociatedAction"> </action> Action中的描述: public final class MyAssociatedAction extends TilesAction { public ActionForward execute( ComponentContext context,ActionMapping mapping,ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{ String title = (String)context.getAttribute( "title" ); System.out.println( "Original title" + title ); context.putAttribute( "title", "New Title" ); return null; }} 如果你使用类名做为controller,就必须继承下面基类或接口中的一个: org.apache.struts.tiles.Controller:这是个controller接口定义了cintorller方法。这些方法接收包含当前Tile内容的参数和普通的servlet参数(request、response和servletContext)。 org.apache.struts.tiles.ControllerSupport:这是个底层类,没有方法。 <tiles:insert page="layout.jsp" controllerClass="org.apache.struts.example.tiles.test.TestTileController" > <tiles:put name="title" value="Test controller set in insert" /> <tiles:put name="header" value="header.jsp" /> <tiles:put name="body" value="body.jsp" /> </tiles:insert> 在definition中使用的例子: <definition name="tileWithActionAsController" path="/actionAsController.do" > <put name="title" value="Title" /> <put name="anAttribute" value="aValue" /> </definition> Action被用作controller: <action path="/ actionAsController " type="org.apache.struts.example.tiles.ActionAsController"> <forward name="failure" path="/failurePage.jsp"/> <forward name="success" path="success.definition"/> </action>四.) 举个例子:RssChannel
在这个例子中,我们将创建一个Tile,通过指定的URL读取Rss channel的内容,再使用一个合适的layout把内容显示出来。Tile产生一个controller和view,controller从XML文件中读取channel内容,然后创建一个容易被view调用的bean做为model。channel feed的URL被当作Tile 的属性值。这样做的优点是:
能够在页面的任何地方插入Tile。 可以在同一个Tile页面中插入多个不同的channel。 只需要简单的修改view就能更改channel。 每个Tile可以放置几个channel,方便使用。注:在jakarta-struts-1.2.2中的tiles-documentation.war有相关的代码。Definition:
tiles-examples-defs.xml------------------------------------------------------------------------------------------------------------------------------------------ <definition name="examples.rssChannel.body" path="/examples/tiles/rssChannels.jsp" controllerUrl="/examples/controller/rssChannel.do" > <putList name="urls" > <!--<add value="http://www.newsforge.com/newsforge.rss" /> --> <add value="http://xmlhack.com/rss.php" /> <add value="http://lwn.net/headlines/rss" /> </putList> </definition> 控制器是一个Action URL,在Structs配置文件中有声明: stucts-examples-config.xml------------------------------------------------------------------------------------------------------------------------------------------ <action path="/controller/rssChannel" type="org.apache.struts.webapp.tiles.rssChannel.RssChannelsAction"> </action> 这个Action没有forward到一个view,相关的view是一个Tile definition。Contorller:controller就是一个java类。它从Tile putList 属性中接收channel URL列表,用RSSDigester类来解析channel的内容。然后把bean中的相关内容做为model,通过Tile 属性传递给view。public final class RssChannelsAction extends TilesAction {
private static Log log = LogFactory.getLog(RssChannelsAction.class); public static final String CHANNELS_KEY = "CHANNELS"; public static final String CHANNEL_URLS_KEY = "urls"; public static final String CHANNEL_URL_KEY = "url";
public ActionForward execute(ComponentContext context,ActionMapping mapping, ActionForm form, HttpServletRequest request,HttpServletResponse response) throws Exception { log.debug("Enter Rss Channel Action"); ActionMessages errors = new ActionMessages(); // -- Retrieve parameters -- // Urls can come from a list, or from a single attribute. List channels = (List) context.getAttribute(CHANNEL_URLS_KEY); if (channels == null) { Object url = context.getAttribute(CHANNEL_URL_KEY); channels = new ArrayList(1); channels.add(url); } log.debug("urls count" + channels.size()); // -- Loop through channels -- List channelBeans = new ArrayList(channels.size()); try { for (int i = 0; i < channels.size(); i++) { RSSDigester digester = new RSSDigester(); String url = (String) channels.get(i); // Add application path if needed if (url.startsWith("/")) { url = toFullUrl(request, url); } log.debug("Channel url=" + url); Channel obj = (Channel) digester.parse(url); log.debug("Channel:" + obj); channelBeans.add(obj); } } catch (Throwable t) { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("rss.access.error")); servlet.log(t.toString()); }
// -- Handle Errors --- if (!errors.isEmpty()) { this.saveErrors(request, errors); // If no input page, use error forwarding log.debug("Exit Rss Channel Action : error"); return null; }
// -- Save Bean, and Continue --- log.debug("Exit Rss Channel Action");
// Use Tile context to pass channels context.putAttribute(CHANNELS_KEY, channelBeans);
return null; }
/** * Compute Full local url from an url starting with "/". */ private String toFullUrl(HttpServletRequest request, String url) { StringBuffer buff = new StringBuffer(); buff.append(request.getScheme()).append("://").append(request.getServerName()); if (request.getServerPort() != 80) { buff.append(":").append(request.getServerPort()); } buff.append(request.getContextPath()).append(url); return buff.toString(); }}
View:把Tile的CHANNELS属性中得到数据迭代显示出来。 <%--/*** Summarize channels as unadorned HTML.** @param CHANNELS List of channels*/--%><%@ page language="java" %><%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %><%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %><%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %><div align="center"><font size="+1"><b><tiles:importAttribute name="CHANNELS" scope="page"/><logic:iterate name="CHANNELS" id="CHANNEL" ><TABLE border="0" cellspacing="0" cellpadding="4" width="100%" align="center" ><TR><TD class="spanhd" ><logic:present name="CHANNEL" property="image"><a href="<bean:write name="CHANNEL" property="link"/>"><img src="<bean:write name="CHANNEL" property="image.URL"/>"></logic:present></a></TD><TD class="spanhd" width="100%"><bean:write name="CHANNEL" property="title"/> <ahref="<bean:write name="CHANNEL" property="link"/>">[home]</a></TD></TR><TD class="yellow" colspan="2"><bean:write name="CHANNEL" property="description"/></TD></TR><TR><TD class="datagrey" colspan="2"><logic:iterate name="CHANNEL" property="items" id="ITEM"><br><b><bean:write name="ITEM" property="title"/></b><br><bean:write name="ITEM" property="description"/><br> [ <a href="<bean:write name="ITEM" property="link"/>">more</a> ]<br></logic:iterate></TD></TR></TABLE><br></logic:iterate></b></font></div> 从这个例子中可以看到,要更换channel,只需改变Tile definition中<putList>的值。总结篇
一.) 常用到的layoutTiles有个令人感兴趣的地方,它可以复用现有的layout,定义一个新的layout,简单改变一下属性设置就可以扩展一个layout。对于一个layout,只不过是以现有页面为基础,抽取并提供一些属性的描述。注:在jakarta-struts-1.2.2中的tiles-documentation.war有相关的代码。
Classic layout这个layout经常会被web站点采用。它提供典型的"header, menu, body ,footer",用<header>和<body>做为HTML页面的骨架,在恰当的地方放置header、menu、body、footer。 所需属性:tiltle - String类型;header、menu、body、footer - URL或definition名称。 调用layout的代码: <tiles:insert page="/layouts/classicLayout.jsp" flush="true"> <tiles:put name="title" value="Browser Title" /> <tiles:put name="header" value="/header.jsp" /> <tiles:put name="footer" value="/footer.jsp" /> <tiles:put name="menu" value="/menu.jsp" /> <tiles:put name="body" value="definition.name" /> </tiles:insert> 也能够在definition中调用: <definition name="mainLayout" path="/layouts/classicLayout.jsp"> <put name="title" value="Browser Title" /> <put name="header" value="/header.jsp" /> <put name="footer" value="/footer.jsp" /> <put name="menu" value="menu.main" /> <put name="body" value="main.portal.body" /> </definition> layout的代码: <HTML> <HEAD> <link rel=stylesheet href="<%=request.getContextPath()%>/layouts/stylesheet.css" type="text/css"> <title><tiles:getAsString name="title"/></title> </HEAD> <body bgcolor="#ffffff" text="#000000" link="#023264" alink="#023264" vlink="#023264"> <table border="0" width="100%" cellspacing="5"> <tr><td colspan="2"><tiles:insert attribute="header" /></td> </tr> <tr><td width="140" valign="top"><tiles:insert attribute='menu'/></td> <td valign="top" align="left"><tiles:insert attribute='body' /></td> </tr> <tr><td colspan="2"><tiles:insert attribute="footer" /></td> </tr> </table></body></html> Menu Layout这个layout用于带有链接的菜单。一个菜单需要一个"item" bean列表,每个"item"包含一个子菜单的数据。可以用一个属性相同的menu layout来代替另一种menu layout(例如可以用vbox layout构建一个垂直的menu,下面会讲到)。 所需属性title - String类型,做为菜单标题(可选)。items - List类型,Items 是带有vlaue、link、tooltip、icon参数的bean。 调用layout的代码: <definition name="examples.menu.settings" path="/layouts/menu.jsp" > <put name="title" value="Preferences" /> <putList name="items" > <item value="my Portal Settings" link="/examples/myPortalSettings.jsp" classtype="org.apache.struts.tiles.beans.SimpleMenuItem" /> <item value="my Menu Settings" link="/examples/myMenuSettings.jsp" classtype="org.apache.struts.tiles.beans.SimpleMenuItem" /> </putList> </definition> definition中描述了两个属性:tilte和list。list是一个item bean 列表,每个item相当于菜单中的一个选项,包含一些参数,value表示选项的标题,link表示链接。 layout的代码: <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %> <%@ page import="java.util.Iterator" %> <%-- Push tiles attributes in page context --%> <tiles:importAttribute /><table> <logic:present name="title"> <tr><th colspan=2> <div align="left"><strong><tiles:getAsString name="title"/></strong></div></th> </tr> </logic:present> <%-- iterate on items list --%> <logic:iterate id="item" name="items" type="org.apache.struts.tiles.beans.MenuItem" > <% // Add site URL if link starts with "/" String link = item.getLink(); if(link.startsWith("/") ) link = request.getContextPath() + link; %> <tr><td width="10" valign="top" ></td> <td valign="top" ><font size="-1"><a href="<%=link%>"> <logic:notPresent name="item" property="icon"><%=item.getValue()%></logic:notPresent> <logic:present name="item" property="icon"> <% // Add site URL if link starts with "/" String icon = item.getIcon(); if(icon.startsWith("/") ) icon = request.getContextPath() + icon; %> <img src='<%=request.getContextPath()%><bean:write name="item" property="icon" scope="page"/>' alt='<bean:write name="item" property="tooltip" scope="page" ignore="true"/>' /> </logic:present></a></font></td> </tr> </logic:iterate></table> VBox 或 VStack Layout这个layout用于垂直的显示Tile列表,常被菜单条或multi - columns layout采用。 所需属性list - 要插入的列表名称或URL 调用的layout代码: <definition name="examples.menu.bar" path="/layouts/vboxLayout.jsp" > <putList name="list" > <add value="examples.userMenu" /> <add value="examples.menu.links" /> <add value="doc.menu.links" /> <add value="examples.menu.settings" /> <add value="doc.menu.taglib.references" /> <add value="doc.menu.printer.friendly" /> <add value="examples.menu.admin" /> </putList> layout代码: <%@ page import="java.util.Iterator"%> <%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %> <tiles:useAttribute id="list" name="list" classname="java.util.List" /> <% // 用Tile 属性初始化一个脚本变量。 // 这里不使用<logic:iterate>标签,因为在JSP1.1不允许这么做。 <% Iterator i=list.iterator(); while( i.hasNext() ) { String name= (String)i.next(); %> <tiles:insert name="<%=name%>" flush="true" /> <br> <% } // end loop %> Multi-columns Layout这个layout垂直显示Tile多行列表。每一行代表一个列表,常用来构建多个Tile主体的入口。 所需属性numCols - 行数list1 - 第一个Tile列表list2、list3、listn - 第n个Tile类别(可选) 调用的代码: <definition name="examples.portal.body" path="/layouts/columnsLayout.jsp" controllerUrl="/portal/myPortal.do" > <put name="numCols" value="2" /> <putList name="list0" > <add value="/examples/tiles/portal/login.jsp" /> <add value="/examples/tiles/portal/messages.jsp" /> <add value="/examples/tiles/portal/newsFeed.jsp" /> </putList> <putList name="list1" > <add value="/examples/tiles/portal/advert3.jsp" /> <add value="/examples/tiles/portal/stocks.jsp" /> <add value="/examples/tiles/portal/whatsNew.jsp" /> <add value="/examples/tiles/portal/advert2.jsp" /> </putList> </definition> layout的代码: <tiles:useAttribute id="numColsStr" name="numCols" classname="java.lang.String" /> <table> <tr> <% int numCols = Integer.parseInt(numColsStr); ComponentContext context = ComponentContext.getContext( request ); for( int i=0; i<numCols; i++ ) { java.util.List list=(java.util.List)context.getAttribute( "list" + i ); pageContext.setAttribute("list", list ); if(list==null) System.out.println( "list is null for " + i ); %> <td valign="top"> <tiles:insert page="/layouts/vboxLayout.jsp" flush="true" > <tiles:put name="list" beanName="list" beanScope="page" /> </tiles:insert> </td> <% } // end loop %> </tr> </table> 这边同样也不能使用<logic:iterate>标签,在JSP1.1不允许在一个标记体内插入另一个页面。 Center Layout这个layout常由"top, left, center, right, bottom"组成,left和right部分是可选的。 所需属性header、body、footer - 用difinition名称或URL显示相应的部分。right、left - 用difinition名称或URL显示相应的部分(可选)。 调用layout的代码:和classic layout类似 <tiles:insert page="/layouts/centerLayout.jsp" flush="true"> <tiles:put name="header" value="/header.jsp" /> <tiles:put name="footer" value="/footer.jsp" /> <tiles:put name="right" value="/menu.jsp" /> <tiles:put name="left" value="/menuLeft.jsp" /> <tiles:put name="body" value="definition.name" /> </tiles:insert> 也可以忽略left和right部分: <tiles:insert page="/layouts/centerLayout.jsp" flush="true"> <tiles:put name="header" value="/header.jsp" /> <tiles:put name="footer" value="/footer.jsp" /> <tiles:put name="body" value="definition.name" /> </tiles:insert> layout的代码: <%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %> <table border="0" width="100%" cellspacing="5"> <tr> <td colspan="3"><tiles:insert attribute="header" /></td> </tr> <tr> <td width="140" valign="top"> <tiles:insert attribute=right ignore='true'/> </td> <td valign="top" align="left"> <tiles:insert attribute='body' /> </td> <td valign="top" align="left"> <tiles:insert attribute='left' ignore='true'/> </td> </tr> <tr> <td colspan="3"> <tiles:insert attribute="footer" /> </td> </tr> </table> Tabs Layout这个layout用表格的形式来呈现Tile类表。Tabs Layout有一个body区域,用来显示当前被选中的Tile;还有一个索引区域,显示可用的表格或Tile。 所需属性tabList - 列表的URL或definition名称,我们用MenuItem来存储数据。selectedIndex - 默认被选中的表格parameterName - HTTP参数名,在HTTP request中存储选中的Tile信息。 调用layout的代码:创建了带有三个索引的tab layout <definition name="examples.tabs.body" path="/layouts/tabsLayout.jsp" > <put name="selectedIndex" value="0" /> <put name="parameterName" value="selected" /> <putList name="tabList" > <item value="Doc Home" link="/index.jsp" classtype="org.apache.struts.tiles.beans.SimpleMenuItem" /> <item value="Quick overview" link="/doc/quickOverview.jsp" classtype="org.apache.struts.tiles.beans.SimpleMenuItem" /> <item value="Tutorial" link="/doc/tutorial.jsp" classtype="org.apache.struts.tiles.beans.SimpleMenuItem" /> </putList> </definition> layout的代码: <%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %> <tiles:useAttribute name="parameterName" classname="java.lang.String" /> <tiles:useAttribute id="selectedIndexStr" name="selectedIndex" ignore="true" classname="java.lang.String" /> <tiles:useAttribute name="tabList" classname="java.util.List" /> <% String selectedColor="#98ABC7"; String notSelectedColor="#C0C0C0"; int index = 0; // Loop index int selectedIndex = 0; // Check if selected come from request parameter try { selectedIndex = Integer.parseInt(selectedIndexStr); selectedIndex = Integer.parseInt(request.getParameter( parameterName )); } catch( java.lang.NumberFormatException ex ) { // do nothing } // Check selectedIndex bounds if( selectedIndex < 0 || selectedIndex >= tabList.size() ) selectedIndex = 0; String selectedBody = ((org.apache.struts.tiles.beans.MenuItem)tabList.get(selectedIndex)).getLink(); // Selected body %><table border="0" cellspacing="0" cellpadding="0"> <%-- Draw tabs --%> <tr><td width="10""> </td> <td> <table border="0" cellspacing="0" cellpadding="5"> <tr><logic:iterate id="tab" name="tabList" type="org.apache.struts.tiles.beans.MenuItem" > <% // compute href String href = request.getRequestURI() + "?"+parameterName + "=" + index; String color = notSelectedColor; if( index == selectedIndex ) { selectedBody = tab.getLink(); color = selectedColor; } // enf if index++; %> <td bgcolor="<%=color%>"><a href="<%=href%>" /><%=tab.getValue()%></a></td> <td width="1" ></td> </logic:iterate> </tr></table></td> <td width="10" > </td></tr> <tr><td height="5" bgcolor="<%=selectedColor%>" colspan="3" > </td></tr> <%-- Draw body --%> <tr><td width="10" bgcolor="<%=selectedColor%>"> </td> <td><tiles:insert name="<%=selectedBody%>" flush="true" /></td> <td width="10" bgcolor="<%=selectedColor%>"> </td></tr> <tr><td height="5" bgcolor="<%=selectedColor%>" colspan="3" > </td> </tr></table>