用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,一切搞定。是否是很简单,我也以为是。