跳转到主要内容

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

所有提交的信息确保安全。

  • 关闭 [x]

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

所有提交的信息确保安全。

  • 关闭 [x]

使用 XPath 在 XForms 中实现炫酷功能

Nicholas Chase (ibmquestions@nicholaschase.com), 自由撰稿人, Backstop Media
Nicholas Chase 参与过 Lucent Technologies、Sun Microsystems、Oracle 和 Tampa Bay Buccaneers 等很多公司网站的开发。Nick 曾当过高中物理教师、低放射性废弃设备管理人员、在线科幻杂志编辑、多媒体工程师、Oracle 讲师和一家交互通信公司的首席技术管。他出版了多部著作,其中包括 XML Primer Plus (Sams)。

简介: 如果您曾经在开发中使用过 XML ,那么您可能对 XPath 有所了解,它是一种表达式语言,让您能够选择 XML 文档的一些部分。如果您使用过 XForms,则肯定熟悉 XPath;构建 XForms 表单时必须使用它!但使用 XPath 您所能做的不仅仅限于选择节点在页面上显示。本文将向您展示 XPath 和 XForms 如何交互以使您能够创建从未想到过的功能,比如用一个简单步骤显示惟一值列表,或结合使用 XPath 和界面元素(比如单选按钮或下拉列表)控制显示的数据,而不仅仅是提交的数据。本文假定您熟悉 XML、XForms 和 XPath 的基础知识。如果您需要重温一下这些内容,则可以在 参考资料 中找到相关链接。

发布日期: 2007 年 9 月 07 日
级别: 中级
访问情况 : 1952 次浏览
评论: 


任务

在本文中,您将创建一个 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 字段。


使用 XPath 表达式自动填充字段

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 所提供功能的一部分。您还可以选择操作现有数据。


使用单选按钮创建 master-detail 表单

使用 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. 列出条目
Listing the entries

此处的重点在于设置页面,以便在用户单击某个单选按钮时,表单可以了解如何将数据从一个实例移动到另一个实例(见清单 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 相对 wherehaving 子句所具有的限制似乎就能难倒他们。所幸的是,这个问题不难解决(见清单 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.zip2KBHTTP

关于下载方法的信息


参考资料

学习

获得产品和技术

讨论

关于作者

Nicholas Chase 参与过 Lucent Technologies、Sun Microsystems、Oracle 和 Tampa Bay Buccaneers 等很多公司网站的开发。Nick 曾当过高中物理教师、低放射性废弃设备管理人员、在线科幻杂志编辑、多媒体工程师、Oracle 讲师和一家交互通信公司的首席技术管。他出版了多部著作,其中包括 XML Primer Plus (Sams)。

关于报告滥用的帮助

报告滥用

谢谢! 此内容已经标识给管理员注意。


关于报告滥用的帮助

报告滥用

报告滥用提交失败。 请稍后重试。


developerWorks:登录


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 使用条款

 


当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

请选择您的昵称:

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

(长度在 3 至 31 个字符之间)


单击提交则表示您同意developerWorks 的条款和条件。 使用条款.

 


为本文评分

评论

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML
ArticleID=256546
ArticleTitle=使用 XPath 在 XForms 中实现炫酷功能
publish-date=09072007
author1-email=ibmquestions@nicholaschase.com
author1-email-cc=dwxed@us.ibm.com

标签

Help
使用 搜索 文本框在 My developerWorks 中查找包含该标签的所有内容。

使用 滑动条 调节标签的数量。

热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。

我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。

使用搜索文本框在 My developerWorks 中查找包含该标签的所有内容。热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。