起初誤會Struts-Menu和Site-Mesh是同一個。Struts-Menu整合Struts1要設定不少東西才能work,不只是在Maven設定Jar,還要下載一堆css、js和圖檔。沒有像Struts2有提供一個quickstart的archtype那樣方便。這次倒過來,先講程式再推到設定。
MenuRepository repository = new MenuRepository(); this.getMenu(list, repository, username, null); ServletContext sc = request.getSession().getServletContext(); MenuRepository defaultRepository = (MenuRepository)sc.getAttribute(MenuRepository.MENU_REPOSITORY_KEY); repository.setDisplayers(defaultRepository.getDisplayers()); request.getSession().setAttribute("repository", repository); … private void getMenu( List<Functions> list, MenuRepository repository, String username, MenuComponent pmc) throws Exception { for (Functions function : list) { List<Functions> listSub = this.functionDao.find(sqlWhere, function.getId(), username); MenuComponent mc = new MenuComponent(); if (pmc != null) mc.setParent(pmc); mc.setTitle(function.getFunctionname()); mc.setName("L" + function.getId()); mc.setLocation(function.getUrl()); if (listSub != null && listSub.size() > 0) { this.getMenu(listSub, repository, username, mc); } repository.addMenu(mc); } } |
上面片段取自Struts1 Action的內容,上半段目的是要把MenuRepository設給Session,再由JSP透過Struts-Menu提供的標籤產出Menu Tree,但寫法是有點怪異(紅字部份),這後面提到配置檔時再解說;下半段則是Struts-Menu核心寫法了:
MenuRepository透過addMenu放一堆MenuTree的節點資訊:MenuComponent,所以有四個必要屬性:
- setName:在MenuRepository的MenuComponent的unique key(為什麼不叫ID?)。
- setTitle:顯示名稱。
- setLocation:設定URL。
- setParent:設定父節點MenuComponent。
對應到JSP寫法如下:
<%@ page pageEncoding="utf-8" language="java"%> <%@ taglib uri="/WEB-INF/tld/jstl/c.tld" prefix="c" %> <%@ taglib uri="/WEB-INF/tld/jstl/fmt.tld" prefix="fmt" %> <%@ taglib uri="/WEB-INF/tld/jstl/fn.tld" prefix="fn" %> <%@ taglib uri="/WEB-INF/tld/struts/struts-html.tld" prefix="html"%> <%@ taglib uri="/WEB-INF/tld/struts-menu/struts-menu.tld" prefix="menu"%> <%@ taglib uri="/WEB-INF/tld/struts-menu/struts-menu-el.tld" prefix="menu-el"%> <HTML> <HEAD> <TITLE>MENU</TITLE> <html:base/> <link href="css/struts-menu/menuExpandable.css" rel="stylesheet" type="text/css" media="screen" /> <link href="css/struts-menu/global.css" rel="stylesheet" type="text/css" media="all" /> <script src="js/struts-menu/menuExpandable.js" type="text/javascript"></script> <link href="css/struts-menu/xtree.css" rel="stylesheet" type="text/css" media="all" /> <script src="js/struts-menu/xtree.js" type="text/javascript"></script> </HEAD> <BODY> <h3>Function List</h3> <script type="text/javascript"> <menu:useMenuDisplayer name="Velocity" config="/templates/xtree.html" repository="repository" bundle=""> if (document.getElementById) { <c:forEach var="menu" items="${repository.topMenus}"> <menu-el:displayMenu name="${menu.name}"/> </c:forEach> } </menu:useMenuDisplayer> </script> </BODY> </HTML> |
紅字為核心所在,是在Java Script使用Struts-Menu的標籤,<menu-useMenuDisplayer>的repository屬性正是被Struts設置的session內容:MenuRepository,而bundle屬性是i18n。所以從上面片段程式看,除了自訂標籤就有struts、struts-menu和公用JSTL之外,還得link一堆css、js。
在配置上也挺複雜的,第一個是要在web.xml加Listener標籤(SiteMesh是加Filter標籤):
<listener> <listener-class>net.sf.navigator.menu.MenuContextListener</listener-class> </listener> |
接著要在struts_config.xml也得加plug-in標籤如下(Struts2好像不用):
<plug-in className="net.sf.navigator.menu.MenuPlugIn"> <set-property property="menuConfig" value="/WEB-INF/menu-config.xml"/> </plug-in> |
從上面得知還得引入一個menu-config.xml,而若JSP有設置bundle屬性,在<plug-in>標籤之前還要設置<message-resources>標籤。在struts-config.xml,標籤是有順序的。其menu-config.xml如下:
<?xml version="1.0" encoding="UTF-8" ?> <MenuConfig> <Displayers> <Displayer name="DropDown" type="net.sf.navigator.displayer.DropDownMenuDisplayer"/> <Displayer name="Simple" type="net.sf.navigator.displayer.SimpleMenuDisplayer"/> <Displayer name="CoolMenu" type="net.sf.navigator.displayer.CoolMenuDisplayer"/> <Displayer name="CoolMenu4" type="net.sf.navigator.displayer.CoolMenuDisplayer4"/> <Displayer name="MenuForm" type="net.sf.navigator.example.PermissionsFormMenuDisplayer"/> <Displayer name="ListMenu" type="net.sf.navigator.displayer.ListMenuDisplayer"/> <Displayer name="TabbedMenu" type="net.sf.navigator.displayer.TabbedMenuDisplayer"/> <Displayer name="Velocity" type="net.sf.navigator.displayer.VelocityMenuDisplayer"/> <Displayer name="XtreeMenu" type="net.sf.navigator.displayer.XtreeMenuDisplayer"/> <Displayer name="CSSListMenu" type="net.sf.navigator.displayer.CSSListMenuDisplayer"/> </Displayers> <Menus> </Menus> </MenuConfig> |
回過頭看JSP的<menu-useMenuDisplayer>的name屬性,是參考到名為Velocity的Displayer,Velocity又是一個Web模板系統,想當然爾要引入它們。最後解釋Struts1 Action在設定MenuRepository為何有點奇怪的地方:宣告一個MenuRepository變數,又要從ServletContext取得一個Default的MenuRepository?目的在於取得menu-context.xml內容值(getDispalyers)設給repository。在web initial時,就被存到特定名稱的session,其session name是MENU_REPOSITORY_KEY的常數。所以JSP就能使用Velocity生成Menu Tree。
所以在pom.xml要引入不少東西,如下:
<dependency> <!-- struts.version= 1.3.10 --> <groupId>org.apache.struts</groupId> <artifactId>struts-core</artifactId> <version>${struts.version}</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts-el</artifactId> <version>${struts.version}</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts-taglib</artifactId> <version>${struts.version}</version> </dependency> <dependency> <groupId>struts-menu</groupId> <artifactId>struts-menu</artifactId> <version>2.4.3</version> </dependency> <!-- velocity --> <dependency> <groupId>velocity</groupId> <artifactId>velocity</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>velocity-tools</groupId> <artifactId>velocity-tools</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>velocity-tools</groupId> <artifactId>velocity-tools-view</artifactId> <version>1.4</version> </dependency> <dependency> <!-- 2009/4/25 --> <groupId>opensymphony</groupId> <artifactId>sitemesh</artifactId> <version>2.4.2</version> </dependency> |
我試過用velocity 1.5版會有點問題,所以還是用1.4,而其它struts-menu要用的js、css等檔案及配置,得自行下載放置,參考
http://struts-menu.sourceforge.net/。嚴格來說,struts-menu做到這樣算很優秀了,若有quickstart的archetype就更好了,缺點就是配置太多了。