在本文中,您将创建一个 report-editing 表单,并使用 XPath 在表单中实现一些炫酷功能。表单中将完成如下功能:
- 使用 XPath 函数产生的结果自动填充节点
- 使用单选按钮创建一个 master-detail 表单
- 只显示列表中的惟一项
- 根据多个条件筛选结果
- 使 XPath 筛选器条件可选
该表单与图 1 类似。
图 1. 最终表单
我们从创建基本表单开始。
首先创建一个表单,要求能够在其中编辑报告的标题和主体(见清单 1)。
清单 1. 基本表单
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/D/tdxhtml1-strict.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:xforms="http://www.w3.org/2002/xforms"
xmlns:ev="http://www.w3.org/2001/xml-events"
>
<head>
<title>Report Entry Editor</title>
<link rel="stylesheet" href="style.css" type="text/css"/>
<xforms:model >
<xforms:instance id="editor" >
<entry xmlns="">
<id />
<status />
<title />
<body />
</entry>
</xforms:instance>
<xforms:bind nodeset="instance('editor')/title" id="titlebind"/>
<xforms:bind nodeset="instance('editor')/body" id="bodybind"/>
<!-- Dummy submission; we're just dealing with the client -->
<xforms:submission id="editsubmission" action="savechanges.php"
method="post"/>
</xforms:model>
</head>
<body>
<h2>Editor</h2>
<xforms:input bind="titlebind" >
<xforms:label><b>Title: </b></xforms:label>
</xforms:input><br/>
<xforms:textarea bind="bodybind">
<xforms:label><b>Body: </b></xforms:label>
</xforms:textarea><br />
<xforms:submit submission="editsubmission">
<xforms:label>Save changes</xforms:label>
</xforms:submit>
</body>
</html>
|
这只是一个基本表单,但是其中有一些元素是编辑器所需的。您可以添加新的报告标题和主体,然后提交表单,但是本文不处理提交。表单显示结果类似于图 2。
图 2. 基本表单
代码中还对一些元素进行了绑定,因此可以直接引用其绑定 id。在本例中,XPath 语句非常简单,但是您可以使用绑定创建 “捷径”,这样就不必在代码中直接使用笨拙的 XPath 语句了;绑定实际上就是为每个 XPath 表达式创建一个 “昵称”。
下一步,创建 entry ID 字段。
entry ID 属性用于识别字段,因此我们需要一个 QName-ish 值来表示它。换言之,就是需要一个不带空格、标点等字符的字符串。为此,您可以在服务器端随机生成字符串,或根据用户输入的数据创建该值(见清单 2)。
清单 2. 自动添加内容
...
<body />
</entry>
</xforms:instance>
<xforms:bind id="fixid" nodeset="instance('editor')/id"
calculate="translate(../title,'ABCDEFGHIJKLMNOPQRSTUVWXYZ ?!',
'abcdefghijklmnopqrstuvwxyz_') "/>
<xforms:bind nodeset="instance('editor')/id" id="sidbind"/>
<xforms:bind nodeset="instance('editor')/title" id="titlebind"/>
<xforms:bind nodeset="instance('editor')/body" id="bodybind"/>
...
<h2>Editor</h2>
<b>Entry ID:</b> <xforms:output bind="sidbind"/><br />
<xforms:input incremental="true" bind="titlebind" >
<xforms:label><b>Title: </b></xforms:label>
</xforms:input><br/>
...
|
在本例中,您创建的绑定不仅可以识别某特定的节点,而且还能为节点赋值。本例中的值为 title 元素中输入的文本,其中的所有大写字母替换为小写,空格替换为下划线,问号和感叹号全部删除。因为您已将标题 input 字段标记为 incremental="true",所以此字段在您键入每个键时都会更新,如图 3 所示。
图 3. 增量式更新字段
在本例中,您使用了 XPath translate() 函数。您不仅可以使用所有 XPath 1.0 定义函数,还可以使用专门为 XForms 创建的函数,比如 now()。XForms 推荐函数包括:
-
if() -
avg() -
min() -
max() -
count-non-empty() -
index() -
property() -
now() -
days-from-date() -
seconds-from-dateTime() -
seconds() -
months()
您可以使用上面的任意函数自动填充实例节点。
但是创建数据只是 XForms 所提供功能的一部分。您还可以选择操作现有数据。
使用 XPath 的一个方便的技巧是创建 “master-detail” 表单。其思想是获取用户单击的单选按钮的标识,并使用它创建 XPath 语句,后者使用适当的数据填充编辑器。要查看此效果,将几个现有条目添加到新实例中(见清单 3)。
清单 3. 使用现有数据
...
<body />
</entry>
</xforms:instance>
<xforms:instance id="ui" xmlns="">
<ui>
<currentItem></currentItem>
<statusChoice> </statusChoice>
<authorId> </authorId>
<category> </category>
</ui>
</xforms:instance>
<xforms:instance id="entries" >
<entries>
<entry xmlns="" id="something_is_broken">
<status>1</status>
<category>General</category>
<author id="Nick">Nick Chase</author>
<title>Something is broken</title>
<body>I hear a lot of rattling around in the boxes when
I shake them up.</body>
</entry>
<entry xmlns="" id="glass_collection_broken">
<status>1</status>
<category>Specific</category>
<author id="Sarah">Sarah Chase</author>
<title>Glass collection broken</title>
<body>Who broke my glass collection? I had it packed in
a box but it looks like somebody just shook the
heck out of it.</body>
</entry>
<entry xmlns="" id="who_am_i">
<status>0</status>
<category>NonSensical</category>
<author id="Nick">Nick Chase</author>
<title>Who am I?</title>
<body>I'm sure there's some reason I'm here, but I just
... can't ... remember...</body>
</entry>
</entries>
</xforms:instance>
<xforms:bind id="fixid" nodeset="instance('editor')/id"
calculate="translate(../title,'ABCDEFGHIJKLMNOPQRSTUVWXYZ ?!',
'abcdefghijklmnopqrstuvwxyz_') "/>
...
<body>
<h2>Existing entries</h2>
<xforms:select1 incremental="true" appearance="full"
ref="instance('ui')/currentItem">
<xforms:itemset nodeset="instance('entries')/entry">
<xforms:label ref="title"/>
<xforms:value ref="@id"/>
</xforms:itemset>
</xforms:select1>
<h2>Editor</h2>
<b>Entry ID:</b> <xforms:output bind="sidbind"/><br />
<xforms:input incremental="true" bind="titlebind" >
<xforms:label><b>Title: </b></xforms:label>
</xforms:input><br/>
|
在本例中,您实际上添加了两个实例。第一个实例 ui 用于保存用户的所有选项。ui 实例中刚开始只存有用户希望编辑的项目,但最终也将使用此实例保存筛选器信息。
条目实例包含了几个现有条目。在本例中,它们拥有相似的编辑器结构,但不作要求,因为您可以手动设置这些值。
该页面还包括了一个现有条目的列表,和一个单选按钮可供用户选择,如图 4 所示。
图 4. 列出条目
此处的重点在于设置页面,以便在用户单击某个单选按钮时,表单可以了解如何将数据从一个实例移动到另一个实例(见清单 4)。
清单 4. 移动数据
...
<xforms:bind nodeset="instance('editor')/body" id="bodybind"/>
<xforms:bind nodeset="instance('editor')/title" calculate=
"instance('entries')/entry[@id=instance('ui')/currentItem]/title"/>
<xforms:bind nodeset="instance('editor')/body" calculate=
"instance('entries')/entry[@id=instance('ui')/currentItem]/body"/>
<xforms:bind nodeset="instance('editor')/@id"
calculate="instance('ui')/currentItem" />
<!-- Dummy submission; we're just dealing with the client -->
<xforms:submission id="editsubmission" action="savechanges.php"
method="post"/>
|
事实上,当您使用 XPath 时,填充编辑器表单很简单。创建三个新的绑定语句,每个引用编辑器实例中的一个节点。然后可以跟以前一样使用 calculate 填充节点,但是在本例中,您将根据 ui 实例 currentItem 的值选择条目实例中的条目。当用户单击单选按钮时可对该值进行设置,如图 5 所示。
图 5. 单击单选按钮
这样编辑器就处理好了。但是,如果拥有的条目超过您的处理能力怎么办?
下一步,创建筛选器字段以便缩小选择范围。首先创建一个可以选择作者的下拉菜单。
创建作者下拉菜单有三种方法:手动创建下拉菜单、创建一个作者名实例,或使用条目实例中的现有作者名。我们使用的是第三种方法(见清单 5)。
清单 5. 按作者筛选
...
<h2>Existing entries</h2>
<xforms:select1 incremental="true" appearance="full"
ref="instance('ui')/currentItem">
<xforms:itemset nodeset="instance('entries')
/entry[author/@id=instance('ui')/authorId]">
<xforms:label ref="title"/>
<xforms:value ref="@id"/>
</xforms:itemset>
</xforms:select1>
<h4>Filter results by:</h4>
<xforms:select1 appearance="minimal" incremental="true"
ref="instance('ui')/authorId">
<xforms:label>Author: </xforms:label>
<xforms:itemset nodeset= "instance('entries')/entry">
<xforms:label ref="author" />
<xforms:value ref="author/@id" />
</xforms:itemset>
</xforms:select1>
<br />
<h2>Editor</h2>
...
|
在本例中,您创建了一个下拉菜单,然后根据下拉菜单的当前值筛选显示的条目,这需要将一个谓词添加到 itemset 节点集中。如图 6 中所示,这种方法存在两个问题:首先,列表显示了重复的作者名。其次,筛选器设置好以后,如果用户还没有选择作者,那么就不会出现条目。
图 6. 列出作者
首先,我们解决第一个问题,重复的作者名。
消除重复对 XPath 新手而言是个头疼的问题,他们大多是以前从事 SQL 编程的人员,predicates 相对 where 和 having 子句所具有的限制似乎就能难倒他们。所幸的是,这个问题不难解决(见清单 6)。
清单 6. 消除重复
...
<xforms:select1 appearance="minimal" incremental="true"
ref="instance('ui')/authorId">
<xforms:label>Author: </xforms:label>
<xforms:itemset nodeset=
"instance('entries')/entry[not(author = preceding-sibling::entry/author)]">
<xforms:label ref="author" />
<xforms:value ref="author/@id" />
</xforms:itemset>
</xforms:select1>
...
|
一般说来,解决这一问题的方法是只选择作者尚未出现的条目元素,实际上您正要完成这个任务。如图 7 所示,问题已经解决。
图 7. 不再有重复
如您所见,当您选择某个作者时,只会出现该作者的条目。之后,您还需要处理条目不出现的问题,但是在这前还要先添加一些其他的筛选器。
添加其他的筛选器只需添加相应的谓词即可(见清单 7)。
清单 7. 添加其他筛选器
...
<h2>Existing entries</h2>
<xforms:select1 incremental="true" appearance="full"
ref="instance('ui')/currentItem">
<xforms:itemset nodeset="instance('entries')/entry[author/@id=instance('ui')
/authorId][category=instance('ui')/category][status=instance('ui')/statusChoice]">
<xforms:label ref="title"/>
<xforms:value ref="@id"/>
</xforms:itemset>
</xforms:select1>
<h4>Filter results by:</h4>
...
<xforms:select1 appearance="minimal" ref="instance('ui')/category">
<xforms:label>Category: </xforms:label>
<xforms:choices>
<xforms:item>
<xforms:label>General</xforms:label>
<xforms:value>General</xforms:value>
</xforms:item>
<xforms:item>
<xforms:label>Specific</xforms:label>
<xforms:value>Specific</xforms:value>
</xforms:item>
<xforms:item>
<xforms:label>NonSensical</xforms:label>
<xforms:value>NonSensical</xforms:value>
</xforms:item>
</xforms:choices>
</xforms:select1>
<br />
<xforms:select1 appearance="minimal"
ref="instance('ui')/statusChoice">
<xforms:label>Status: </xforms:label>
<xforms:choices>
<xforms:item>
<xforms:label>Active</xforms:label>
<xforms:value>1</xforms:value>
</xforms:item>
<xforms:item>
<xforms:label>Inactive</xforms:label>
<xforms:value>0</xforms:value>
</xforms:item>
</xforms:choices>
</xforms:select1>
<h2>Editor</h2>
...
|
现在您又添加了两个下拉菜单,静态创建,微妙之处在于定义项目集的 XPath 语句。您可以向语句中添加任意数量的谓词。在本例中,语句选择满足作者筛选器的所有条目,然后从中选择满足类别筛选器的条目,继而从中选择满足状态筛选器的条目,如图 8 所示。
图 8. 筛选器正常运行
使用此方法,您可以根据要求从任意数量的不同位置获取条目。
还有一个问题没有解决。只有用户激活筛选器后才能选择条件,因此用户必须选择所有三个筛选器才能得到所有的结果。您也许认为可以使用 “通配符” 作为默认值,但是 XPath 连通配符也不识别。您要使用一点小技巧处理这个问题(见清单 8)。
清单 8. 使用默认值
...
<xforms:instance id="ui" xmlns="">
<ui>
<currentItem></currentItem>
<statusChoice> </statusChoice>
<authorId> </authorId>
<category> </category>
</ui>
</xforms:instance>
...
<h2>Existing entries</h2>
<xforms:select1 incremental="true" appearance="full"
ref="instance('ui')/currentItem">
<xforms:itemset nodeset="instance('entries')/entry
[author/@id=instance('ui')/authorId or instance('ui')/authorId=' ']
[category=instance('ui')/category or instance('ui')/category=' ']
[status=instance('ui')/statusChoice or
instance('ui')/statusChoice=' ']">
<xforms:label ref="title"/>
<xforms:value ref="@id"/>
</xforms:itemset>
</xforms:select1>
<h4>Filter results by:</h4>
<xforms:select1 appearance="minimal" incremental="true"
ref="instance('ui')/authorId">
<xforms:label>Author: </xforms:label>
<xforms:choices>
<xforms:item>
<xforms:label>All</xforms:label>
<xforms:value> </xforms:value>
</xforms:item>
</xforms:choices>
<xforms:itemset nodeset=
"instance('entries')/entry[not(author = preceding-sibling::entry/author)]">
<xforms:label ref="author" />
<xforms:value ref="author/@id" />
</xforms:itemset>
</xforms:select1>
<br />
<xforms:select1 appearance="minimal" ref="instance('ui')/category">
<xforms:label>Category: </xforms:label>
<xforms:choices>
<xforms:item>
<xforms:label>All</xforms:label>
<xforms:value> </xforms:value>
</xforms:item>
<xforms:item>
<xforms:label>General</xforms:label>
<xforms:value>General</xforms:value>
</xforms:item>
...
</xforms:choices>
</xforms:select1>
<br />
<xforms:select1 appearance="minimal"
ref="instance('ui')/statusChoice">
<xforms:label>Status: </xforms:label>
<xforms:choices>
<xforms:item>
<xforms:label>All</xforms:label>
<xforms:value> </xforms:value>
</xforms:item>
<xforms:item>
<xforms:label>Active</xforms:label>
...
|
特别注意:XPath 语句在此处分为多行只是为了适合页面。在实际操作时您 “必须” 将它们放入一行才能正常运行。
实际上,现在才算是真正的开始。状态下拉菜单有一个新选项 All,其值为一个空格。ui 实例现在使用一个空格作为 statusChoice 元素的值,因此开始显示的就是这个值。类别下拉列表也添加了相同的代码。
作者下拉列表稍微复杂一些。项目集只适用于动态创建的项目;可以选择定义一些静态项目。所幸的是,这两种项目都可以使用。
所有一切准备好以后,就该构造 XPath 语句显示条目了。
谓词的工作原理类似于:对于每个节点,引擎使用上下文处理谓词。如果表达式为 true,节点就添加到结果中。反之则不然。因此此处您完成的工作就是创建一种情形,其中,如果用户为某个特定的筛选器选择了一个值,那么只有适当的节点返回 “true”。另一方面,如果筛选器仍然设为 “All”,那么 “or” 表达式的第二部分将一直为 true,因此所有的节点都会通过测试。
您可以在图 9 中看见最终页面。
图 9. 最终页面
看起来很正常。
XForms 严重依赖于使用 XPath 所能实现的功能。所幸的是,XPath 可以实现的功能很多。在本文中,您了解了如何使用 XPath 实现如下功能:
- 使用 XPath 函数的结果自动填充节点
- 根据用户在一个位置做出的选择在另一个位置选择数据
- 只显示列表中的惟一项
- 根据多个条件筛选结果
- 给 XPath 表达式提供通配符值
您在此看到的内容可能很简单,但是这只是表面现象。使用这些技术可以不加修改地创建最复杂的 XForms 用户界面。
| 描述 | 名字 | 大小 | 下载方法 |
|---|---|---|---|
| 文章源代码 | xpathinxforms_source.zip | 2KB | HTTP |
学习
- 您可以参阅本文在 developerWorks 全球网站上的 英文原文。
-
获取 XForms 的基本介绍:XForms 简介,第 1 部分: 新的 Web 表单标准(Chris Herborth,developerWorks,2006 年 12 月)、XForms 简介,第 2 部分: 表单、模型、控件和提交动(Chris Herborth,developerWorks,2006 年 12 月)和 XForms 简介,第 3 部分: 使用动作和事件(Chris Herborth,developerWorks,2006 年 12 月)。
-
在 IBM developerWorks 中国网站 XML 专区 了解关于 XForms 的更多内容。
-
查看 XForms 的发展方向。
-
查看 XPath 的发展方向。
-
了解创建下一代表单的要点:XForms 基础(Nicholas Chase,developerWorks,2006 年 11 月)。
-
查看在实际应用程序中使用这些技术的极端例子,阅读 developerWorks 系列文章 使用 Xforms 创建会计工具。
-
访问 IBM XForms 社区。
-
下载 XForms 的 Mozilla 扩展。
-
查看如何接收 XForms 数据:Accepting XForms data in Java(Nicholas Chase,developerWorks,2006 年 10 月)、Accepting XForms data in Perl(Tyler Anderson,developerWorks,2006 年 10 月)和 Accepting XForms data in PHP(Nicholas Chase,developerWorks,2006 年 9 月)。
-
IBM XML 认证:了解如何才能成为一名 IBM 认证的 XML 和其他相关技术的开发人员。
-
XML 技术文档库:访问 developerWorks XML Zone,获取大量的技术技术文章、技巧、教程、标准和 IBM 红皮书。
-
developerWorks 技术事件和网络广播:随时关注各种技术的最近动态。
-
在 developerWorks 中国网站 XML 专区 了解关于 XML 的所有信息。
获得产品和技术
-
XForms Recommendation 由 W3C 维护。
-
阅读 XPath Recommendation。
讨论