前两节我们复习了Struts2的数据标签、流程管理标签以及其他标签,本节将学习Struts2的UI组件标签。每一个UI组件都是一个功能单元,用户通过UI组件与应用程序交互、向应用程序输入数据。每一个Struts 2 UI组件的核心都是一个HTML表单控件。但是不要误解,这些组件不仅仅是一个输出HTML输入元素的标签,它们是高层组件,其中的HTML元素只是在浏览器中的表现。UI组件构建在一个分层的、迷你的MVC架构上,这个架构把某个组件的HTML标签的输出分离到了底层的FreeMarker模板中,开发人员可以通过修改这些底层的模板来改变这些组件。

1.为什么需要UI组件标签

新一波的前端界面复杂化的浪潮已然到来,随着时间的流逝,由AJAX和其他富客户端技术支持的动态的前端界面慢慢变成了普通的需求。一个新的web应用程序框架如果不能从框架上考虑管理前端界面日益增加的复杂度,那么它必将走向失败。Struts2 UI组件很好的实现了这个要求。

不仅仅是表单元素

每一个常用的HTML表单元素都有一个对应的Struts 2 UI组件,除了这个一对一的关系,更复杂的UI组件将一对HTML元素包装成一个单元来创建一个新的表单组件。你可以把呈现对应的HTML元素当作是UI组件的基本功能,但最终你会发现,UI组件不仅仅是底层HTML元素的总和。下面是UI组件能够做的一些事情。

  • 生成HTML标记
  • 绑定HTML表单字段和Java端属性
  • 与框架提供的类型转换关联起来
  • 与框架提供的验证关联起来
  • 与框架提供的国际化功能关联起来

1.生成HTML标记

每一个UI组件标签确实生成了一组HTML标记,这些标记定义了对应的HTML元素,并且经常包含一些其他的布局标记,这是组件标签最简单的一个方面。下面是一个简单的示例。:

<s:textfield name="username" label="Username"/>

上述标签将会在页面输出如下HTML代码:

<td class="tdLabel">
    <label for="Register_username" class="label">Username:</label>
</td>
<td>
    <input type="text" name="username" value="" id="Register_username"/>
</td>

首先不要被表格标记迷惑,这些是XHTML主题生成的,主题也追加了一些CSS控制用的类属性。如果你喜欢,它甚至能够为这些样式生成一个简单的默认样式表。id属性由包含它的表单的name属性与输入字段的name属性拼接而成。

2.将表单字段绑定到ValueStack的属性

创建HTML元素只是开始。一旦HTML元素生成后,UI组件会将这些生成的元素与Struts2框架提供的丰富的功能连接起来。首先,UI组件会把表单字段绑定到ValueStack的属性,这为数据转移奠定了基础。当你查看这些UI组件标签生成的表单时,它在两个方向上与后台的Java属性关联。首先,ValueStack上的数据能够流向表单来预填充表单。其次,当表单提交时,来源于表单字段的数据会流向框架并自动被转移到ValueStack上。UI组件通过将name属性指定为OGNL表达式将自己同时绑定到ValueStack上的传出和传入属性,以实现表单的预先填充和表单提交时的自动数据转移。

3.与类型转换、数据验证以及国际化集成

对于类型转换和数据验证,在问题发生时UI组件能够自动处理错误消息。类型转换或者数据验证出现问题时会导致请求将用户带回数据表单页面。这时UI组件会自动检测与自己相关的错误信息是否存在,并显示相应的错误信息,当然你也可以自定义和国际化这些错误信息。

UI组件也能够接入国际化机制为UI组件提供本地化的标签名字。这个特性需要使用所有UI组件都有的key属性,只需设置这个属性,UI组件就能够从框架的ResourceBundle中取得本地化的标签,并能智能的推算出name属性和value属性来完成组件的数据绑定。

2. 标签、模板和主题

现在我们来学习UI组件标签独一无二的架构。UI组件的迷你MVC架构最大的好处是可以重用自定义的内容。

UI组件架构.png

UI组件构建在分层的架构上。组件API被公开为页面中的JSP标签,它会被作为JSP标签处理。本地标签仅仅是框架对应的UI组件API的包装,如JSP类型的Struts 2 textfield标签就是对org.apache.struts2.components.TextField组件类的包装。如图,本地标签很快将控制转交给对应的组件API做实际处理。UI组件层处理标签的逻辑并准备数据,之后组件将呈现的任务交给底层的FreeMarker模板。

以这种方式将标签API输出的标记与实现逻辑的Java类分层,这样我们就有了迷你MVC架构。像所有的MVC架构一样,最主要的好处就是能够很容易的修改标签的视图而不需要修改嵌入在标签组件类中的业务逻辑。

2.1 标签

标签非常简单,Struts2标签API是一个高层的API,也就是说,标签的功能和使用定义在给定的视图层技术细节之外,框架提供了标签API在JSP、Velocity、FreeMarker等视图层技术中的实现。不管你使用哪种技术,本地标签都将处理过程委托给UI组件框架,框架管理标签的逻辑和数据模型,数据可能来源于ActionContext或者ValueStack,也可能来源于标签的属性或参数。那些呈现UI组件的HTML标记的模板可以访问这些数据。框架使用FreeMarker模板呈现UI组件。

2.2 模板

每一个标签都有一个与之对应的FreeMarker模板来呈现它的标记。FreeMarker模板看起来很像普通的文本文件,在这些普通的文本内部有一些特殊的FreeMarker指令,它们可以动态地取得数据并能呈现结果输出。对于UI组件标签,模板获得标签逻辑收集的数据模型,将这些数据和模板中的静态部分混合起来创建最终呈现在HTML页面中的标记。

2.3 主题

底层模板为给定标签生成标记是很容易理解的,然而,实际上情况更复杂。实际上,每个标签都准备了几个版本的底层模板。每一个版本属于一个主题。一个主题是一组模板,每一个模板至少属于一个标签,这些模板以某种一致的方式或者习惯呈现这些标签。

有几种方法可以指定使用的主题,默认情况下,所有的标签都使用xhtml主题呈现。可以在整个应用的范围内改变默认主题,也可以在每个页面或者每个标签指定不同的主题。

Struts 2 自带了4个主题来呈现UI组件,它们如下:

  • simple:呈现基本的HTML元素
  • xhtml:使用表格提供的布局呈现UI组件
  • css_xhtml:使用纯CSS提供的布局呈现UI组件
  • ajax:扩展xhtml主题并且提供丰富的ajax组件。

改变主题

所有框架的默认属性都定义在default.properties文件中,这个文件在Struts 2核心的JAR文件的org.apache.struts2包内。覆盖框架的默认属性,只需创建自己的属性文件,命名为struts.properties,并把它放在类路径中。要改变默认主题,只需添加如下内容,UI组件就会使用css_xhtml来呈现。

struts.ui.theme=css_xhtml