在这篇初级读物中,我们将介绍 XForms 的一些最有用的方面,并向您介绍一个简单的应用程序示例。本文基于 2002 年 7 月发布的 XForms 1.0 工作草案(请参阅 参考资料以获得该链接以及其它有用的链接)。
历史概述
XForms 包含的某些概念可以追溯到标准通用标记语言(Standardized General Markup Language (SGML))。SGML 的意图之一是通过将文本内容嵌入到机器可读的标记中来区分文本内容及其表示。这样做的意义之一就是内容和表示可以由不同的人开发。它还引入了重用的灵活 性。
超 文本标记语言(HTML)是一种 SGML 文档类型,它也将内容包括在有意义的标记中。然而,HTML 没有延续将内容与表示分离的概念。用 HTML 创建 Web 页面的相对简单性使得它在因特网基础结构广泛可用的同时也几乎被全世界采用。HTML 标记的标准化产生了一种能被不同的 Web 浏览器所理解的语言。然而,随着商业目标驱使 Web 开发人员力图制作更复杂和更动态的站点,该语言的局限就突现出来了。
清单 1. 示例 HTML 表单
|
在 清单 1 中,请注意如何在创建用户界面的同一代码中指定 name="HairColor"
和 value="Brown"
。该 HTML 片段还“硬编码”将在用户界面中显示的窗口控件的类型(本例中是单选按钮)。这样,在不支持单选按钮的平台上就不能正确地呈现该表单。因为 HTML 表单可以包括嵌入的数据和硬编码的决定,所以更改单独表单的表示并不容易。HTML 的标准化还意味着 HTML 标记集并不旨在由个别开发人员进行扩展,这限制了定制该语言或提高其表达能力的可能性。
可扩展标记语言(Extensible Markup Language (XML))和可扩展样式表语言(Extensible Stylesheet Language(XSL,一种 XML 文档类型))的引入就是试图解决这些问题。不象 HTML,XML 文档与 XSL 转换的结合支持数据与表示的分离。类似 name
和 value
的字段可以存储为 XML 数据,而通过使用 XSL 转换将 XML 文档转换成 HTML 可以创建表示。这使得开发人员可以避开许多使用 HTML 表单时无法避免的硬编码决定。通常通过使用不同的 XSL 样式表,可以用不同的样式表示同一 XML 数据;反过来,单个 XSL 转换可以用统一的样式呈现不统一的 XML。此外,XML/XSL 给予开发人员 HTML 不曾给予的可扩展性。
尽管通过向 XML 内容应用 XSL 转换所生成的 HTML 比起静态 HTML 有许多优点,但功能的增加是有代价的。首先,负责处理由 XML 向 HTML 转换的服务器的性能不及只提供静态 HTML 的服务器。转换本身需要服务器周期;另外,表单可能需要多个客户机-服务器往返周期。其次,用 XSL 转换实现 XML 要求开发人员具有一些技能,这些技能不象那些只实现 HTML 所需的技能那样广泛可用。
XForms:下一代 Web 表单
如上所述,为了使 HTML 文档中包括更丰富和更动态的表单的同时使创建 Web 表单更容易更方便,对这样的可扩展方法的需求推动了 XForms 规范。可以把 XForms 看做 XHTML(一种符合 XML 格式的 HTML)的扩展模块。XForms 规范引入了一组预定义的标记、元素和属性,它们可以简化 Web 表单的创建。具备 XForms 处理器的浏览器可以在客户机上呈现 XForms 文档。这使开发人员能够避免更新 Web 表单时与服务器往返周期相关的性能代价。(如果您愿意的话,也可以用服务器端 XForms 解释器将 XForms 文档转换成 HTML)。最重要的是,XForms 分隔表单的数据模型、视图和控制器。这些部分被进一步分解为更精细的可重用层。例如,XForms 将表单的视图区分为 表示(presentation)和 用途(purpose)。
XForms 的数据模型部分使您能独立于任何用来显示数据值的窗口控件集,来声明数据项和结构。XForms 独立于数据模式本身的声明,来定义将这些数据项绑定至显示窗口控件的方法。此外,还定义了一些声明性方法,这些方法是对任何特定数据项的值进行更改的操作。
XForms 窗口控件实质上是抽象的,因此不同的平台可以选用不同的方法实现它们。例如,尽管 HTML 中 <radioButton>
标记在各平台间可能只有一种 表示(如下拉菜单),但 XForms 中 <select1>
窗口控件(请参阅 附录 C4)可能在 PC 浏览器上产生下拉菜单而在 PDA 上产生单选按钮的列表。然而, <select1>
控件的 用途仍旧相同 ― 它为用户提供从一组项目中选择单个元素的能力。
Hello World:XForms 简介
在本文中,我们要处理一些基本 XForms 标记的重要方面。我们会介绍标记的用法和语法,以及它们的一些常用属性的意义。当我们讨论特定标记时,我们会给出至本文的 附录的链接,您可以在其中查看完整的规范。
首先,我们介绍惯用的“Hello World”示例以便您对表单结构和 XForms 语法有一个初步的了解。本文还提供了比较 XForms 与其它标记语言的方法。在随后的章节中,我们会介绍作为本教程主要示例的电子商业订货表单的某些部分。
清单 2显示了完整的“Hello World”示例, 图 1则显示示例是如何在与 XForms 兼容的浏览器中呈现的。注意文档的基本结构很重要,因为每个 XForms 文档都有相似的布局。您将看到模型是在头内定义的(请参阅 XForms 模型),而视图是在文档的主体内定义的(请参阅 XForms 用户界面)。还要注意名称空间在每个 HTML 标记内指定。为避免命名冲突,您应尽可能随时使用名称空间前缀。
清单 2. Hello World
|
图 1. “Hello World”的呈现
请注意,由于 XForms 语言目前没有产品级质量的处理器,所以所有示例的呈现都是图示说明而不是来自特定的 XForms 实现。(有关 XForms 处理器 beta 发行版的链接,请参阅 参考资料)。
本文的余下部分主要讨论一个电子商业订货表单示例。该订货表单包括输入客户信息、计算价格信息和列出购物车中的项目等部分。本教程的第一个部分介绍模型定义的整个过程,第二个部分说明视图的定义。示例代码完整地包含在 附录 A中。 图 2显示了完成的订货表单示例的呈现情形。
图 2. 呈现的订货表单
XForms 模型
XForms 模型表示表单的内容,因此不会随着表示而更改。模型由表单的数据和逻辑组件组成。表单的数据包含在 实例(instance)(马上有更多介绍)中;它包括所有的表单字段以及任何必需的临时存储器。表单的逻辑组件定义其行为。这些逻辑组件是用 事件处理程序、数据绑定和 提交信息定义的,在后面的章节中将详细介绍所有内容。
XForms 允许在同一表单内定义多个模型。这在您希望一个应用程序支持多个功能时非常有用。例如,一个个人门户可能由证券报价机模型、日历模型等组成。如果您定义了多个模型,可以使用 id
属性唯一地标识其中的每一项。 清单 3是订货表单示例的框架代码:
清单 3. 订货表单示例的框架代码
|
如前所述,您可以看到在文档的头内定义了模型。下一步是声明实例数据。
有关 XForms 模型的规范信息,请参阅 附录 B6。
实例(instance)
实例数据在模型内定义。它代表所有传递到后端的信息和模型内需要的任何临时存储器。然而,每个实例只能包含一个 XML 数据树结构 ― 换句话说,只能有一个根节点。
实例或在本地模型内定义,或通过 URI 指向远程机器上现有的 XML 数据。 清单 4 在订货表单中定义了一个名为 OrderInfo
的本地实例。
清单 4. 定义本地实例
|
在 清单 4 中, OrderInfo
有四个主要组件:
PersonalInfo
包含所有您希望从用户获得的信息PriceInfo
包括计算帐单所需的信息TaxInfo
包含有关各州税率(为简单起见,本示例只定义纽约州、康涅狄格州和新泽西州的税率)的静态信息ShoppingCart
是用户打算购买的项目的列表
传统上,人们会把表单数据简单地看作表单从用户处获取的信息。然而, PriceInfo
和 TaxInfo
节突出说明了如何将实例数据分别用作临时变量和常数信息。
作为另一种选择, 清单 5说明如何通过引用远程 XML 资源来实例化一个实例。
清单 5. 引用远程实例
|
有关实例数据的规范信息,请参阅 附录 B5。
绑定、相关性和约束
在 XForms 中,您可以用两种方法将值绑定至实例数据:可以使数据与其它数据相关,或将数据绑定至用户提供的输入。本章介绍前一种方法:后一种方法 ― 用户输入绑定 ― 在 XForms 用户界面中讨论。
<bind>
标记在模型内定义;它允许您使实例数据与其它数据相关。例如, 清单 6 中的语句将 TaxTotal
绑定到已计算的税款( TaxRate
乘 SubTotal
):
清单 6. bind 语句
|
ref
属性引用您希望绑定的节点 ― TaxTotal
。 calculate
属性定义您希望绑定到节点的值;在本例中,您将 TaxTotal
绑定为 TaxRate
和 SubTotal
的乘积。除了乘法以外, calculate
还支持其它简单的函数(请参阅 附录 D以获得完整列表)。
您可以使用 relevant
属性来定义使 bind
标记有效或应用该标记的条件。例如,您可以象 清单 7 中显示的那样绑定 TaxRate
:
清单 7. 带 relevant 属性的 bind 语句
|
这条 <bind>
语句 仅当用户居住在康涅狄格州( relevant
条件)时,才将 TaxRate
( ref
)赋值为康涅狄格州税率( calculate
)。您需要为纽约州和新泽西州定义相似的语句,以确保在任何给定的情况下将 TaxRate
绑定至适当的州的值。
您也可以使用 <bind>
标记来以各种方法约束实例数据 ― 例如,通过用 type
属性设置数据类型。 清单 8 演示如何约束 SubTotal
字段以使它始终为十进制数:
清单 8. 带 type 常量的 bind 语句
|
有许多其它约束实例数据的方法;请参阅 附录 E以获得完整列表。XForms 处理器根据实例数据的约束逐个字段地对其进行检查。如果用户输入的数据违反了任何数据约束,支持 XForms 的浏览器会立即给用户以反馈。这就确保了后端进程仅接收格式良好的数据,并且极大地提高了可用性的级别。
有关绑定、相关性和约束的规范信息,请参阅 附录 B2。
submission
若没有定义一些与后端服务器通信的方法,订货表单模型就不是完整的。通过使用 action
属性,您可以使用 <submission>
标记来指定当用户准备提交时要进行的调用,如 清单 9中所示:
清单 9. submission 语句
|
您还可以指定 HTTP 方法,即: get
或 post
。为了封装 XML 内容,我们建议您始终使用 post
。
同一模型中可以有多个 <submission>
块。这样,您需要确保使用 id
属性给每个 <submission>
标记一个唯一的标识符(例如, id="sub"
),因为在用户界面中这些标记将被绑定至提交按钮(请参阅 submit)。
有关 <submission>
的规范信息,请参阅 附录 B9。
XForms 用户界面
用户界面定义如何将数据模型实例并入页面的表示。它是通过同时使用 原子控件(如 <input>
和 <output>
)和 复合控件(如 <group>
、 <repeat>
和 <switch>
)来表示的。原子组件是实际的窗口组件,开发人员可用它来填充表单,组合 UI 组件则可用来对原子对象进行组织和分组。正如前面提到的那样,原子控件和组合控件都不象其 HTML 对应物那样绑定至特定的表示,而是与特定的抽象用途相关。
复合控件
<group>
、 <repeat>
和 <switch>
标记提供将原子表单控件组合到复杂的用户界面中的方法。例如,在大型电子商业表单中,您可以将一些原子对象分组成各节 ― 例如,地址节、付款节和项目节。然后您可以获取这些 group,并把它们当作复合组件。也可以嵌套这些复合对象来创建其它复合控件。
group
group是最简单的复合控件:它是将控件组织在一起的基本容器。 清单 10演示如何将不同的表单控件组织在一起,以便以后在较大的电子商业表单中将这个 group 作为对象使用。
清单 10. 地址表单示例的视图框架
|
有关 group 的规范信息,请参阅 附录 B3。
repeat
<repeat>
标记通过允许开发人员将用户界面模板应用到具有相同类型和结构的实例数据的集合,帮助缩减表单中冗余代码的数量。在订货表单示例中,我们使用 <repeat>
标记显示购物车中的项目,如 图 3中所示:
图 3. repeat 节
我们没有显式地为每件项目编写一个 UI 控件,而是使用 <repeat>
标记重复 清单 4 中实例中的 ShoppingCart
节。
清单 11. repeat 语句示例
|
清单 11 中 repeat
元素的 nodeset
属性定义了根据模型实例计算的 XPath。 repeat
元素迭代这个 nodeset
,以获得它的每个嵌套控件。
在 清单 11 中, ref
属性引用 ProductInfo
子树中的字段值。您也可以通过在 XPath 前面附加一个 @
来引用属性值。例如,您可以用下面的调用在每个 ProductInfo
中引用 name
属性:
|
开发人员可以使用 <repeat>
标记以使他们的 XForms 文档更短并且对实际数据实例更敏感。此外,该标记还可以增强 XForms 语言的表达能力:您不 需要确切地知道购物车中有多少项目; <repeat>
标记会自动地遍历实例树然后产生每个可能匹配的输出。
有关 <repeat>
的规范信息,请参阅 附录 B8。
switch
<switch>
语句允许动态的 UI 生成。在每个 <switch>
语句内, <case>
语句定义应使用不同 UI 的条件。例如,在电子商业表单内,在获取个人信息后,供应商希望了解付款方法。 <switch>
标记可用来根据用户选择的付款类型进行分支选择。如果用户选择用信用卡付款,则会显示一组动态生成的 UI 控件。或者,如果用户选择现金支付或其它付款方法,则表单会生成另一组可视化组件。 <switch>
标记允许 UI 是用户驱动的。
有关 <switch>
的规范信息,请参阅 附录 B11。
原子表单控件
原子控件是开发人员可用来设计用户界面的基本组件。您可以用这些控件来填充刚才描述的那三种高级控件。让我们研究几个原子控件。
input
最基本的表单控件是 <input>
标记。正如在 绑定、相关性和约束中提到的那样,这是将用户提供的数据绑定至实例的方法之一。 清单 12 中显示了我们地址表单中 <input>
标记的示例:
清单 12. input 语句
|
ref
属性指定输入数据应该绑定的 XPath。因为 ref
属性用 XPaths 定位实例位置,如果有多个节点匹配(例如,多个 /AddressInfo/Address/Zip
节点),那么除非开发人员显式地指定要选择的索引,否则 ref
会选择第一个匹配。在我们的示例中,用户对该 input 字段输入的值绑定至 /OrderInfo/PersonalInfo/Address/ZipCode
。必须通过创建 <input>
标记的 <label>
子项来添加 input 字段的标题。(请注意, <label>
标记是一些可选标记中的一个,这些可选标记可以在每个表单控件内创建。有关这些标记的完整列表,请参阅 附录 F)。
有关 <input>
的规范信息,请参阅 附录 B4。
output
<output>
只显示实例数据的值。在 清单 13 中的示例中,您可以使用 <output>
控件打印已计算的税款:
清单 13. output 语句
|
该标记在很多方面与 <input>
类似;唯一不同的是 <output>
是只读的。
有关 <output>
的规范信息,请参阅 附录 B7。
action
<action>
标记是 XML 事件处理程序。它由一组声明性 XML 处理程序填充,这些处理程序能控制大多数标准用例,从而消除了对复杂脚本的需求。那些脚本通常是冗长且复杂的,而且不一定能被不同的平台所支持。通过将 XML 事件封装到一组易于调用的标记中,就容易包括动态内容并保证其在所有支持 XForms 的平台上都能正常工作。
event
属性指定 <action>
标记应该侦听哪个事件。例如, 清单 14 演示如何在我们的订货表单中定义 refreshForm
操作。
清单 14. action 语句
|
当出现 click
事件时,操作事件处理程序会捕捉该事件并调用 <refresh>
操作。
同一个 <action>
标记内有多个操作是有可能的。当事件处理程序被激活时,它会调用其主体内定义的所有操作。(有关 XForms 声明性操作的完整列表,请参阅 附录 G)。当把这些常用操作置入 <action>
标记时,这些操作允许客户机端浏览器生成内容以响应用户驱动的事件。也可以在模型内定义 <action>
标记。在下一节中,您将看到如何在表单控件内嵌入 <action>
标记。
有关 <action>
的规范信息,请参阅 附录 B1。
trigger
trigger 是在被激活时执行操作的 UI 控件。例如,它们可以是响应用户的鼠标点击的按钮。当被激活时,trigger 会发出 XML 事件,该事件会被由 <action>
标记定义的事件处理程序捕捉。您可以通过生成类似 清单 15中的语句在 XForms 中定义 trigger:
清单 15. 无事件的 trigger 语句
|
然而,该 trigger 不做任何事情。要让该 trigger 做一些事情,您需要在其中嵌入类似在 action中定义的操作(请参阅 清单 16)。
清单 16. 执行某些操作的 trigger 语句
|
当被点击时,该 trigger 抛出 click
事件,该事件由 <action>
标记事件处理程序捕捉。这调用了 <refresh>
操作。
有关 <trigger>
的规范信息,请参阅 附录 B12。
submit
<submit>
标记创建这样的触发器:当用户单击时,该触发器会调用在 <submission>
标记内定义的操作。(这事实上是触发器的别名,该触发器包含与特定 <submission>
标记相关的分派事件)。
清单 17. submit 语句
|
有关 <submit>
的规范信息,请参阅 附录 B10。
结束语
通过使用 XForms,您在定义 Web 表单时可以顺利地将用途与表示分离。您可以将主要精力放在表单的内容和收集的数据上,而只需较少地注意表示的样式。该语言定义了功能强大的事件模型,从而可以不再需要定制脚本以处理简单的与表单相关的任务。
通 过使用 XForms,开发人员主要关注的是要收集的数据。通过使用标准 XML 模式,可以显式地定义数据的结构和类型。XForms 通过允许附加的约束和相关性的规范扩展了该模型。XForms 处理器无需额外代码即可计算并强制这些约束。处理器在提交数据进行处理 之前检查数据类型和约束。
XForms 规范还允许通过数据驱动的条件性创建动态表单。您不必再编写特殊代码来生成基于用户响应的定制表单。XForms 可以在收集到的数据和条件改变时实时地修改表单。
对 XForms 的导航由 XForms 事件模型处理,这与客户机呈现无关。对于同一 XForms,您可以在一台客户机上将其表示成一个页面而在另一台上将其表示成多个页面,而不必为保存状态以及表示适当的导航控件而担心。
由于其简明的规范和功能强大的特性,用 XForms 创建的表单往往比传统 Web 表单更易于维护。代码并没有与表示标记混杂在一起。额外的数据类型检查代码是不必要的。数据结构与表单表示标记分离。
- 无论 XForms 是否真的是分离用途和表示的 最终解决方案,XForms 的确是下一代的 Web 表单。