级别: 高级 Kevin Kelly (kekelly@us.ibm.com), 技术团队资深成员, IBM Jan J. Kratky (kratky@us.ibm.com), 顾问软件工程师, IBM Steve K Speicher (sspeiche@us.ibm.com), 资深软件工程师, IBM
2006 年 12 月 15 日
XForms 是一种从各种支持 Web 的平台采集和提交数据的理想的开放标准技术。通过一个输入表单从多个数据源采集数据很常见。但是,表单中填写的每个位置可能都有自己特殊的站点提交需求,比如提交到表单作者并不知道的多个目的地。多重提交目标包括本地存储位置、调交到只读的 “vault” 以便审计或者记录日志,或者其他特殊的提交目标。使用 JavaScript™ 编辑 DOM 和 XForms 允许一个表单适应多重的、站点特有的提交需求。
背景
我们往往需要单独设计一个表单,以便从多个不同的来源收集标准数据集。单独一个全能的表单非常适合特定的数据采集要求,因为可以约束数据的输入值,保证数据完整性,而且也很容易对比和汇总不同来源的数据。对于这类数据驱动的表单,XForms 是一种很好的选择,因为它是一种可以在多种支持 Web 的平台上运行的开放标准。
 |
XForms
XForms 是数据模型驱动的 Web 表单的 W3C 标准。
图 1 中的 XForms 文档是在 Mozilla Firefox 1.5 中呈现的结果,使用 Mozilla 为 Firefox 开放的 Firefox XForms 扩展。
有关 XForms 的链接请参阅 参考资料 部分。
|
|
为了采集数据,XForms 文档必须有多个提交目标,或者输入完成后用户点击提交按钮时数据需要发送到或者保存到的位置。但是,每个表单填写位置可能有特殊的提交需求,比如保存提交表单的本地副本、提交表单到只读的位置或者记录下每次提交的 “vault”,以便进行审计或记录日志。这些特殊的提交需求可能降低表单的价值,因为单个表单不能封装各表单填写位置的特殊提交需求。通过使用 JavaScript for Document Object Model(DOM)编程,运行时即可在浏览器将提交目标与 XForms 文档联系起来,从而将提交需求与标准表单分离开来,这样每个表单填写处可以有任意多个特殊的提交目标。
本文中使用的完整 XForms 文档、模式、XML 数据实例和样式表可以参见 下载 部分。
示例 XForms 文档
假设有一个长期医疗研究项目,参与者定期会晤,交流一些可能遇到的症状。为定期会诊记录病例症状的简单表单如 图 1 中的 XForms 文档所示。
图 1. XForms 表示的简单病理报告
这个简单的例子中只有三个输入字段:Subject ID 字符串、xs:date 类型的日期(使用 XForms 呈现的日期选择器限制输入的值)和枚举类型的症状(无、发烧、肌肉疼痛、关节僵硬、恶心)。该 XForms 文档提交实例的模式定义如 清单 1 所示。
清单 1. 症状报告模式
...
<xs:simpleType name="incidentType">
<xs:restriction base="xs:string">
<xs:enumeration value="none" />
<xs:enumeration value="fever" />
<xs:enumeration value="muscle pain" />
<xs:enumeration value="joint stiffness" />
<xs:enumeration value="nausea" />
</xs:restriction>
</xs:simpleType>
<xs:complexType name="incidentReportType">
<xs:sequence>
<xs:element name="theSubjectID" type="xs:string" maxOccurs="1" />
<xs:element name="theDate" type="xs:date" maxOccurs="1" />
<xs:element name="theSymptom" type="incidentType" maxOccurs="1" />
</xs:sequence>
</xs:complexType>
<xs:element name="theIncident" type="incidentReportType"/>
...
|
因为每个字段背后都有一个模式定义明确规定了类型,除非所有数据都对模式有效,否则 XForms 可能会禁止提交输入数据,以确保所有提交点上数据的同质性。该模式中,theDate 被定义为 xs:date
类型,theSymptom 则定义为 incidentType 类型。此外,maxOccurs 属性进一步限制在提交的数据实例中每个输入字段只能有一个。清单 2 给出了该表单提交实例的一个例子。
清单 2. XML 实例
...
<theIncident xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="theIncident.xsd">
<theSubjectID>7D</theSubjectID>
<theDate>2006-10-13</theDate>
<theSymptom>none</theSymptom>
</theIncident>
...
|
静态 XForms 提交
图 1 中的 XForms 表单有一个提交按钮,用户在输入完成后可以提交表单。这个提交按钮的激活可以和 XForms 数据模型专用的提交动作结合起来。该例中的数据模型是根据 清单 1 模式定义的一个如 清单 2 所示的实例。在 XForms 文档中,这个提交动作(可以是多个)和应用的模型与实例一起定义。比如,清单 3 中定义了两个提交:remoteSave 提交将把 XML 实例 theIncidentInstance
提交给 xformstest.org 回显服务,localSave 则在本地磁盘上保存 theIncidentInstance 的副本。
清单 3. 包含提交定义的 XForms 模型定义
...
<xforms:model id="theIncidentModel" schema="theIncident.xsd">
<xforms:instance id="theIncidentInstance" src="theIncident.xml"/>
<xforms:submission id="remoteSave"
action="http://xformstest.org/cgi-bin/showinstance.sh"
method="post"/>
<xforms:submission id="localSave" action="./local_save.xml"
method="put"/>
</xforms:model>
...
|
使用一个简单的 XForms 触发器创建一个提交按钮,激活的时候调用两个(还可以更多)提交动作,如 清单 4 所示。
清单 4. 提交按钮的触发器
...
<xforms:trigger>
<xforms:label>Submit</xforms:label>
<xforms:action ev:event="DOMActivate">
<xforms:send submission="remoteSave"/>
<xforms:send submission="localSave"/>
</xforms:action>
</xforms:trigger>
...
|
这是一个有效的表单,但要求表单作者知道表单填写者的本地提交需求(像本地磁盘保存一样),对于在多个地点使用的表单这种方法不容易改变。需要一种办法允许当地的表单填写者能够独立于表单和提交实例定义本地提交需求。
动态 XForms 提交
XForms 数据模型机制和用于文档对象模型(DOM)的 JavaScript 编程提供了一种可伸缩的解决方案,不需要修改表单,但是允许每个提交点灵活定义任意多个特殊的提交需求。这种解决方案的第一部分是定义和包含提交动作的单独的数据模型,XForms 很容易做到。可制订一个简单的模式来保存提交动作信息,比如提交的目标、惟一标识提交动作的标识符和调用方法(如 put 或 post),如 清单 5 所示。
清单 5. 提交动作模式
...
<xs:simpleType name="methodType">
<xs:restriction base="xs:string">
<xs:enumeration value="put" />
<xs:enumeration value="post" />
</xs:restriction>
</xs:simpleType>
<xs:complexType name="actionType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="theMethod" type="methodType" />
<xs:attribute name="theId" type="xs:string" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="submitListType" >
<xs:sequenceminOccurs="0"maxOccurs="unbounded">
<xs:element name="theAction" type="actionType" />
</xs:sequence>
</xs:complexType>
...
|
该模式将允许创建使用多重提交填充单独数据模型的实例。可以发送包含默认提交设置的基本数据模型然后在每个站点上进行编辑。清单 3 所示 “硬编码” XForms 文档中包含两个提交选项的那个具体例子,可用作 清单 6 所示的 XForms 数据模型。
清单 6. 提交动作实例
...
<theSubmitList>
<theAction theMethod="post" theId="remoteSave">
http://xformstest.org/cgi-bin/showinstance.s</theAction>
<theAction theMethod="put" theId="localSave">./local_save.xml</theAction>
</theSubmitList>
...
|
有了独立的提交数据模型之后,就可以使用 JavaScript 编写 DOM 代码在运行的时候把要提交的实例模型和本地提交目标联系起来。JavaScript 代码和包含“空”触发器的两个模型如 清单 7 所示。要注意,theIncidentModel 不再像 清单 3 所示的静态、硬编码提交模型那样包含提交定义。
清单 7. 动态 XForms 提交的 JavaScript 代码
...
<xforms:model id="theSubmitModel" schema="theSubmits.xsd">
<xforms:instance id="theSubmitInstance" src="theSubmits.xml"/>
</xforms:model>
<xforms:model id="theIncidentModel" schema="theIncident.xsd">
<xforms:instance id="theIncidentInstance" src="theIncident.xml"/>
</xforms:model>
<script type="">
function buildSubmits()
{
var oModel = document.getElementById("theSubmitModel");
var oInstance = oModel.getInstanceDocument("theSubmitInstance");
var pModel = document.getElementById("theIncidentModel");
var pInstance = pModel.getInstanceDocument("theIncidentInstance");
var theSubmits = oInstance.getElementsByTagName("theSubmits");
var theSubmitList = theSubmits[0].getElementsByTagName("theSubmitList");
var theAction = theSubmitList[0].getElementsByTagName("theAction");
var submitTrigger = document.getElementById("dynamicSubmit");
for(var i = 0; i < theAction.length ;i++) {
n = document.createElementNS("http://www.w3.org/2002/xforms", "xforms:submission");
n.setAttribute("id" , theAction[i].getAttribute("theId"));
n.setAttribute("method" , theAction[i].getAttribute("theMethod"));
n.setAttribute("action" , theAction[i].textContent);
pModel.appendChild(n);
m = document.createElementNS("http://www.w3.org/2002/xforms", "xforms:send");
m.setAttribute("submission" , theAction[i].getAttribute("theId")) ;
submitTrigger.appendChild(m);
}
}
</script>
...
<xforms:trigger>
<xforms:label>Submit</xforms:label>
<xforms:action ev:event="DOMActivate" id="dynamicSubmit">
</xforms:action>
</xforms:trigger>
<script type="">
buildSubmits();
</script>
...
|
对应两个 XForms 数据模型,JavaScript 建立了两个模型和实例变量,即 theSubmitModel 和 theIncidentModel,然后遍历 theSubmitModel 实例,在 DOM 中向 theIncidentModel 实例的 XForms 模型定义增加提交,向 XForms 提交按钮触发器增加提交动作。要修改的触发器必须惟一标识,这里触发器用 ID dynamicSubmit 惟一地标识它。这些修改都在 DOM 内存中完成,永远不会写到磁盘上的 XForms 代码本身之中,但是可以用 DOM Inspector 在 DOM 中观察到,如 图 2 所示。
 |
DOM
文档对象模型或者 DOM 是一种平台和语言中立接口,程序和脚本可以通过它动态访问更新文档的内容、结构和样式。
图 2 所示的 DOM Inspector 是 FireBug 0.4,Mozilla Firefox 1.5 的一种扩展。对于 DOM 编程和调试非常方便。
相关的 DOM 链接请参阅 参考资料部分。
|
|
图 2. DOM 中显示的动态提交
提交编辑的表单
由于提交保存在单独的数据模型和磁盘文件中,这个单独的提交实例都成为其自身的 XForms 文档的候选项,后者很容易从 XML 实例中生成并使用 XML Forms Generator(XML Forms Generator Eclipse 插件的链接请参阅 参考资料部分)建立自己的模式。得到的 XForms 文档如 图 3 所示。
图 3. 提交编辑 XForms
结束语
如果需要创建数据驱动的、能够对 XML 实例数据提交应用模式驱动约束的、从多个需要迅速进行关联和汇总的不同来源采集同质数据的表单,XForms 无疑是一种极其出色的可选技术。通过将表单填写方站点的独特数据提交需求与逻辑表单本身和提交的实例分离开来,即可控制一个表单和一组数据输入约束,提供了多重、特定于站点的灵活提交选择。要将表单 DOM 和多重、特定于站点的提交需求相分离,然后再在运行时连接起来,可通过 JavaScript 编程完成,即在 DOM 中,从单独的模型读取多个提交选项,并将这些选项写入 XForms 提交的实例模型和 XForms 提交按钮触发器。
XForms 1.0 需要以 DOM 为依据使用 JavaScript 编程,来支持这种动态的多重提交目标场合,不过制定 XForms 1.1 工作草案的 W3C XForms
工作组正在考虑一种声明性解决方案。具体来说,就是为 XForms submission 增加一个 resource 子元素,允许用它所包含的 URI 来根据实例数据动态计算提交。
下载 | 描述 | 名字 | 大小 | 下载方法 |
|---|
| 本文完整示例代码 | dynamicXFormsSubmit.zip | 5KB | HTTP |
|---|
参考资料 学习
获得产品和技术
讨论
作者简介  | |  | Kevin E. Kelly 是一位资深的 IBM 技术人员,研究 Web 和医疗保健业的新软件标准。Kelly 先生是万维网联盟(W3C)Compound Document Formats(CDF)工作组的主席、W3C Hypertext Coordination Group 成员,还曾经是 W3C XForms 工作组的成员。他还是 Health Level 7(HL7)组织的成员,参与了 HL7 和对象管理组(OMG)的面向服务的架构(SOA)项目。他的主要研究方向是开发通过基于 XML 和模型驱动的技术更快、更有效地采用的基于开放标准的技术。 |
 | |  | Jan Joseph Kratky 是开发 XML Forms Generator 和 Visual XForms Designer 的技术主管,W3C XForms 工作组成员。Kratky 先生是一位 Sun 认证的 Java 程序员和 Web 组件开发人员,从 1997 年开始使用 Java 技术,2001 年开始接触 Eclipse 技术。他目前是位于北卡罗来纳 Research Triangle Park 的 IBM Emerging Software Standards 的一名软件工程师。 |
 | |  | Steve Speicher 是一位从事新标准研究的 IBM 高级软件工程师,复合 XML 文档工具箱(Compound XML Document Toolkit)的首席开发人员。Speicher 先生是 W3C Compound Document Formats (CDF) 工作组的成员,他利用模型驱动的开发(MDD)来改进标准的开发。以前他曾经在 Rational 部门和 IBM 内部工具部门研究 “构建” 和软件配置管理(SCM)工具。 |
对本文的评价
|