Freemarker中訪問Struts2的select標籤的問題以及解決辦法

用Struts2作MVC,Freemarker作表現層。由於Freemarker中能夠訪問Struts2的一些標籤,在加上Freemarker自己的一些功能,我的以爲這種組合非常方便。html

可是這裏碰到一個問題:我參考Struts2的標籤說明文檔,作了一個下拉框。這個下拉框默認就是三個值,一個空,一個是,一個否。用Struts2的select標籤很容易解決。JSP代碼以下:java

<s:select name="person.is_emergency" list="#{'-1':'','0':'是','1':'否'}" value="-1"/>apache

注意,這裏採用了一個特殊的處理方式,在頁面上來生成一個HashMap,OGNL的表達式方式。#{}服務器

你們能夠看一下Struts2的Tag Reference中,關於select標籤的說明有以下的例子:app

<s:select label="Months" name="months" headerKey="-1" headerValue="Select Month" list="#{'01':'Jan', '02':'Feb', [...]}" value="selectedMonth" required="true" />ide

 

如今問題來了,在Freemarker中,對#{}的使用有本身的含義:輸出一個數字值,能夠按照格式進行輸出。使用的格式以下:ui

<#assign x=2.582>
#{x; m1M2} <#-- 輸出2.58 -->

相關資料能夠參考http://freemarker.sourceforge.net/docs/ref_depr_numerical_interpolation.html。this

這樣一來,若是在ftl中使用struts2的select標籤,就不能用#{}這種方式來生成一個默認的內容了。不然會跟Freemarker的標籤衝突。ftl模板首先會把這個內容看成數字輸出來解析,而後發現其中有引號和非數字類東西,就會報錯。lua

 

freemarker.core.ParseException: Error on line 33, column 137 in ftl/sys/PersonList.ftl
Found string literal: '-1'
Expecting: number in ftl/sys/PersonList.ftl
	at freemarker.core.FMParser.notStringLiteral(FMParser.java:84)
	at freemarker.core.FMParser.numberLiteralOnly(FMParser.java:147)
	at freemarker.core.FMParser.NumericalOutput(FMParser.java:1077)
	at freemarker.core.FMParser.FreeMarkerText(FMParser.java:2705)
	at freemarker.core.StringLiteral.checkInterpolation(StringLiteral.java:75)
	at freemarker.core.FMParser.StringLiteral(FMParser.java:946)
	at freemarker.core.FMParser.PrimaryExpression(FMParser.java:243)
	at freemarker.core.FMParser.UnaryExpression(FMParser.java:319)
	at freemarker.core.FMParser.MultiplicativeExpression(FMParser.java:435)
	at freemarker.core.FMParser.AdditiveExpression(FMParser.java:385)
	at freemarker.core.FMParser.RangeExpression(FMParser.java:556)
	at freemarker.core.FMParser.RelationalExpression(FMParser.java:511)
	at freemarker.core.FMParser.EqualityExpression(FMParser.java:476)
	at freemarker.core.FMParser.AndExpression(FMParser.java:585)
	at freemarker.core.FMParser.OrExpression(FMParser.java:608)
	at freemarker.core.FMParser.Expression(FMParser.java:221)
	at freemarker.core.FMParser.NamedArgs(FMParser.java:2048)
	at freemarker.core.FMParser.UnifiedMacroTransform(FMParser.java:1904)
	at freemarker.core.FMParser.FreemarkerDirective(FMParser.java:2399)
	at freemarker.core.FMParser.Content(FMParser.java:2618)
	at freemarker.core.FMParser.OptionalBlock(FMParser.java:2786)
	at freemarker.core.FMParser.UnifiedMacroTransform(FMParser.java:1972)
	at freemarker.core.FMParser.FreemarkerDirective(FMParser.java:2399)
	at freemarker.core.FMParser.Content(FMParser.java:2618)
	at freemarker.core.FMParser.OptionalBlock(FMParser.java:2786).......

 

而後,我去翻閱了Struts2中關於Freemarker的Tag使用說明文檔。其中有這種描述:原文地址spa

Remember that all tag attributes must first be set as Strings - they are then later evaluated (using OGNL) to a different type, such as List, int, or boolean. This generally works just fine, but it can be limiting when using FreeMarker which provides more advanced ways to apply attributes. Suppose the following example:

<@s.select label="Foo label - ${foo}" name="${name}" list="%{{1, 2, 3}}"/>

What will happen here is that each attribute will be evaluated to a String as best it can. This may involve calling the toString method on the internal FreeMarker objects. In this case, all objects will end up being exactly what you would expect. Then, when the tag runs, the list attribute will be converted from a String to a List using OGNL's advanced collection support.

But suppose you wish to use FreeMarker's list or hash support instead? You can do this:

<@s.select label="Foo label - ${foo}" name="${name}" list=[1, 2, 3]/>

Notice that the list attribute no longer has quotes around it. Now it will come in to the tag as an object that can't easily be converted to a String. Normally, the tag would just call toString, which would return "[1, 2, 3]" and be unable to be converted back to a List by OGNL. Rather than go through all this back and forth, the frameworks's FreeMarker tag support will recognize collections and not pass them through the normal tag attribute. Instead, the framework will set them directly in the parameters Map, ready to be consumed by the template.

In the end, everything tends to do what you would expect, but it can help to understand the difference of when OGNL is being used and when it isn't, and how attribute types get converted.

他給出了一種用List作爲select參數的寫法,可是Hash呢?????他沒有說明。

我試驗用這種方式來處理寫成['-1':'','0':'是','1':'否']各類形式都不行......

後來我就採用了一個折中的辦法,經過在服務器上生成一個Session或者Application參數,來保存這個Hash,而後ftl模板經過#application來訪問。這樣竟然能夠了。

首先,在Login的Action中,訪問並設定Application參數:

 

ActionContext context = ActionContext.getContext();

			HashMap yn = new HashMap();
			yn.put("-1", "");
			yn.put("0", "是");
			yn.put("1", "否");

			context.getApplication().put("yesno", yn);

 

而後在ftl模板中以下方式訪問:

 

<@s.select name="person.deleted" list="#application.yesno" value="-1"/>

 

 

可是這裏有兩個問題:

1.全部的Select值都要用這種方式定義在Application中,豈不是......至關的不衛生!:(

2.這種方式生成的Hash,順序徹底是不可控制的。有可能下拉框第一個值是「否」,第二個是"空",第三個是"是",這.......

 

最終在今天早上的時候,忽然想到,既然Freemarker要解析這個東西,那麼我就不讓他解析,我把它看成字符串參數按照原樣傳遞給Struts2的Taglib,這樣是否是可行呢?

 

<@s.select name="person.deleted" list=r"#{'-1':'','0':'是','1':'否'}"  value="-1"/>

OK,一切搞定。是否是很簡單,我也以爲是。

相關文章
相關標籤/搜索