跳转到主要内容

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

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

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

  • 关闭 [x]

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

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

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

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

  • 关闭 [x]

使用 JavaScript 让 XForms 变得更健壮

Michael Galpin (mike.sr@gmail.com), 软件工程师, eBay
Michael Galpin 自 1998 年以来一直从事 Web 应用程序专业开发。他是加州圣何塞市易趣公司的软件工程师。他拥有加州理工学院的数学学位。

简介: 您是否遇到过这种情形:在 XForm 中单击 Remove 按钮,直至所有行消失,接着尝试向其中插入一行?结果如何?什么也没有发生!本文将会介绍如何使用 JavaScript 解决这个问题。通过本文,了解如何使用一个由触发器调用的智能 JavaScript 函数替代标准的删除操作。其中您还会了解到如何聪明地使用 JavaScript 操作模型的数据。

发布日期: 2007 年 8 月 13 日
级别: 中级
访问情况 : 2749 次浏览
评论: 


先决条件

本文只基于 XForms 和 JavaScript。针对安装在 Mozilla Firefox 2.0 之上的 Mozilla XForms 插件 进行测试。使用的是标准的 XForms 和 JavaScript,因此应该在这两个标准技术的其他实现上运行。没有使用服务器端的技术。

典型的表示例

我们来看一个典型的 XForms 示例。它展示了如何创建表示 XML 文档中重复节点的表。尤其展示了如何使用 XForms 执行聚集计算以及如何使用 XForms 添加或删除模型数据的节点,同步地自动保存视图。查看清单 1 中的完整源代码。


清单 1. 典型的 XForms 表示例
                
<?xml version="1.0" encoding="UTF-8"?>
<xhtml:html xmlns:ev="http://www.w3.org/2001/xml-events" 
xmlns:xforms="http://www.w3.org/2002/xforms" 
xmlns:xhtml="http://www.w3.org/1999/xhtml" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <xhtml:head>
        <xhtml:title>Demonstration of table with 
                                      column total</xhtml:title>
        <xf:model id="my-model" xmlns="http://www.w3.org/1999/xhtml"
 xmlns:xf="http://www.w3.org/2002/xforms">
            <xf:instance id="my-data" src="my-data.xml" xmlns=""/>
            <xf:bind calculate="sum(../Item/Amount)" nodeset="/Data/Total"/>
            <xf:submission action="my-data.xml" id="update-from-local-file"
 instance="my-data" method="get" replace="instance"/>
            <xf:submission action="my-data.xml" id="view-xml-instance"
 method="get"/>
            <xf:submission action="my-data.xml" id="save-to-local-file"
 method="put"/>
        </xf:model>
    </xhtml:head>
    <xhtml:body>
        <xf:group ref="/Data" xmlns="http://www.w3.org/1999/xhtml" 
xmlns:xf="http://www.w3.org/2002/xforms">
            <xf:label/>
            <xf:repeat id="repeatItem" nodeset="Item" 
xmlns="http://www.w3.org/1999/xhtml">
                <xf:input class="item-description" id="description-input" 
ref="Description" xmlns="http://www.w3.org/1999/xhtml">
                    <xf:label/>
                </xf:input>
                <xf:input class="item-amount" ref="Amount" 
xmlns="http://www.w3.org/1999/xhtml">
                    <xf:label/>
                </xf:input>
            </xf:repeat>
            <xhtml:div id="sum">
            <xf:output ref="/Data/Total" xmlns="http://www.w3.org/1999/xhtml">
                <xf:label/>
            </xf:output>
            </xhtml:div>
            <xf:trigger id="insertbutton" xmlns="http://www.w3.org/1999/xhtml">
                <xf:label>Add Item</xf:label>
                <xf:action ev:event="DOMActivate">
                    <xf:insert at="last()" nodeset="Item[last()]" 
position="after"/>
                    <xf:setvalue ref="Item[last()]/Description" value="''"/>
                    <xf:setvalue ref="Item[last()]/Amount" value="0"/>
                    <xf:setfocus control="description-input"/>
                </xf:action>
            </xf:trigger>
            <xf:trigger id="delete" xmlns="http://www.w3.org/1999/xhtml">
                <xf:label>Delete Item</xf:label>
                <xf:delete at="index('repeatItem')" ev:event="DOMActivate" 
nodeset="Item[index('repeatItem')]"/>
            </xf:trigger>
            <xf:submit submission="update-from-local-file" 
xmlns="http://www.w3.org/1999/xhtml">
                <xf:label>Reload</xf:label>
            </xf:submit>
            <xf:submit submission="save-to-local-file" 
xmlns="http://www.w3.org/1999/xhtml">
                <xf:label>Save</xf:label>
            </xf:submit>
            <xf:submit submission="view-xml-instance" 
xmlns="http://www.w3.org/1999/xhtml">
                <xf:label>View XML Instance</xf:label>
            </xf:submit>
        </xf:group>
    </xhtml:body>
</xhtml:html>

您可能注意到了,在源代码中模型中的数据来自外部 XML 文件。该文件如清单 2 所示。


清单 2. 示例的 XML 数据
                
<?xml version="1.0" encoding="UTF-8"?>
<Data>
   <Item>
      <Description>Furniture</Description>
      <Amount>1000</Amount>
   </Item>
   <Item>
      <Description>Dock</Description>
      <Amount>2000</Amount>
   </Item>
   <Item>
      <Description>Boat</Description>
      <Amount>3000</Amount>
   </Item>
   <Item>
      <Description>Lawn equipment</Description>
      <Amount>4000</Amount>
   </Item>
   <Item>
      <Description>Hot tub</Description>
      <Amount>5000</Amount>
   </Item>
   <Total>15000</Total>
</Data>


运行示例

简单地在 Web 浏览器中打开示例即可运行它。您应该看到类似图 1 所示的内容。


图 1. 典型的 XForms 示例
典型的 XForms 示例

尝试此示例,注意,您可以使用显示的 Add ItemDelete Item 按钮添加和删除行。例如,如果您单击一次 Delete Item 按钮,您应看到类似图 2 的内容。


图 2. 删除了一个项目
删除了一个项目

注意,不仅是删除了顶部的项目,而且项目总数也重新进行了计算。这很好地说明了 XForms 的强大功能。再单击 Delete Item 按钮四次,您应看到类似图 3 的内容。


图 3. 删除了所有项目
删除了所有项目

这不怎么好看,但是可以单击 Add Item 按钮开始重新输入数据,对吧?但结果是在这种情况下单击 Add Item 按钮不起任何作用。


问题出在哪儿?

为什么 Add Item 在这种情况下不起作用?如果您查看 源代码,Add Item 按钮导致向模型中进行插入,使用 XForms 插入命令添加一条记录。而那条记录刚好是一个 “Item” 节点,因此 Add Item 操作接着为 Item 的 Description 和 Amount 节点设置一些默认值。Add Item 通过实际上克隆结构中的最后一个节点以定义插入节点的类型。nodeset="Item[last()]" 正是实现此操作。这正是 “bug” 的来源。如果您消除了所有项目,那么就没有节点可供克隆因而也就无法执行插入。因此 Add Item 按钮会失效。当然,现在的问题是,如何修正这个问题?


修正

正如软件工程中的大多数问题一样,有很多方法可以解决此处描述的问题。这里要阐述的解决方案是使用 JavaScript。您的策略是更改删除的方式。在 清单 1 中您会发现 Delete Item 按钮使用了 XForms 删除命令。您将使用对一个 JavaScript 函数的调用替代对此命令的调用。该函数需要与 XForm 模型交互。您将只修改对 Delete Item 按钮的调用,因此无需更改 Add Item 按钮。我们来查看一下 JavaScript 解决方案。


JavaScript

正如前面提到的,方法是只更改 Delete Item 按钮,而不是 Add Item 按钮。因此要想使 Add Item 按钮起作用,决不能删除模型中的所有数据。因此您将跟踪周围的项目个数,当您删除至最后一个时,不要继续删除。只是使用默认内容替代该项目的内容,好像您执行了 Add Item 按钮一样。我们来查看一下清单 3 中的 JavaScript 代码。


清单 3. JavaScript deleteItem() 函数
                
<xhtml:script type="text/javascript">
                 //<![CDATA[
                      function deleteItem(){
                           var model = document.getElementById("my-model");
                           var instance = model.getInstanceDocument("my-data");
                           var dataElement = instance.getElementsByTagName("Data")[0];
                           var itemElements = dataElement.getElementsByTagName("Item");
                           var cnt = itemElements.length;
                           if (cnt > 1){
                                dataElement.removeChild(itemElements[cnt-1]);
                           } else {
                                // last element so just set its data to default vals
                                var descripElement = 
itemElements[0].getElementsByTagName("Description")[0];
                                descripElement.childNodes[0].nodeValue = "";
                                var amtElement = 
itemElements[0].getElementsByTagName("Amount")[0];
                                amtElement.childNodes[0].nodeValue = "0";
                           }
                           model.rebuild();
                           model.recalculate();
                           model.refresh();
                      }
                 //]]>
            </xhtml:script>

下面介绍其工作原理。您需要从 JavaScript 中访问 XForms 模型。所幸的是,使用 JavaScript 的 DOM API 即可轻松地实现访问。XForms 模型是页面 DOM 的一部分,因此您只需使用 document.getElementById() 方法,正如您访问 HTML div 或 HTML 输入字段那样。

当您使用 document.getElementById() 访问 XForms 模型时,如 清单 3 所示,您得到的是 nsIXFormsModelElement。此对象中含有几个非常有用的方法,包括上面使用的 getInstanceDocument() 方法。这样您就可以访问 清单 1 中定义的 XForms 实例。这是一个 DOM 对象,表示 清单 2 中的 XML 文档。因此在 JavaScript 代码中,您只需让 DOM 获取 Item 元素即可。您确定模型中有多少个 Item 并将其存储在 cnt 变量中。很明显,此处要处理两个用例。第一个是您拥有不止一个 Item,第二个是您只有一个 Item。在第一个用例中,您需要删除项目。


使用 JavaScript 删除项目

那么如何使用 JavaScript 而不是 XForms 删除控件删除一条 Item 记录呢?解决方案极其简单。先前调用的 getInstanceDocument() 方法给您提供一个真正的 DOM 对象。因此您可以对此对象执行任何对其他 DOM 对象执行的操作。它支持全部的 DOM API 功能。因此您简单地对 DOM 元素使用 removeChild() 方法。删除 index (cnt - 1) 处的 Item,因为您的元素数组(调用 getElementsByTagName("Item") 获得)是 0-indexed。这里没有什么神奇的 XForms API,只有简单的 DOM 编程。


删除最后一个项目

您已经重新生成了使用 XForms 删除命令能够正常完成的逻辑。这样做的目的是为了更优雅地处理删除最后一个项目时的情况。现在查看一下这个临界情况,比如 cnt == 1

正如前面提到的,此处的关键在于您并不希望实际删除最后一个项目。如果删除,则 Add Item 动作中的 XForms 插入命令将不再有效。当然,您也可以重新编码,但是尽量避免这样做。

因此不是删除最后一个项目,而是使用一个空白 Item 替代该项目的内容。这个 Item 的类型与您单击 Add Item 按钮时得到的 Item 类型相同。例如,它的 Description 为一个空字符串,而它的 Amount 为 0。为此,您再次使用 DOM API。您只访问最后一个 Item 元素,然后访问该元素的 Description 和 Amount 元素。我们将其文本节点的值分别设为空字符串和 0。


让视图和模型同步

此时,您已经处理了 Delete Item 的两个用例。还有一点工作要做。通常情况下,当您使用 XForms 命令修改 XForms 模型时,所有的重新计算和视图刷新都是自动完成的。使用 JavaScript 访问这些内容时情况却并非如此。您需要手动完成这些工作。

所幸的是,在 deleteItem() 函数开始部分,您引用的 nsIXFormsModelElement 对象含有几个有用的方法可以帮助解决问题。首先使用的是它的 rebuild() 方法。此方法让它重建模型中数据的内部表示。它实际上让模型对象与 DOM 同步。这完全不会影响视图,只是对模型有影响。

接下来您需要使用模型的 recalculate() 方法。原因在于您要使用模型中的 XForms bind-calculate 命令跟踪表中 amount 的总数。当您调用 recalculate() 方法时会刷新该计算。通用,这只是让模型与 DOM 同步。完全不影响视图。

既然模型和数据已经同步了,您可以重新绘制视图。为此,您对模型调用 refresh() 方法。此方法导致绑定到模型的所有控件被刷新。对于删除一行而言,这将使该行消失,对于删除最后一行,这将导致最后一行的数据更改为指定的空白数据。在这两种情况下,这将使显示的项目总数基于当前数据得到更新。


触发 JavaScript

既然已经编写了智能的 JavaScript 函数处理删除,只需修改 XForm,使它在调用到 Delete Item 按钮时调用此 JavaScript 就可大功告成了。为此,只需修改 Delete Item 的 XForms 声明,如清单 4 所示。


清单 4. 新的 XForm delete item 控件
                
<xf:trigger id="delete" xmlns="http://www.w3.org/1999/xhtml">
                <xf:label>Delete Item</xf:label>
                <xf:load ev:event="DOMActivate"
 resource="javascript:deleteItem()"/>
            </xf:trigger>

注意,您只是将 XForms delete 命令替换为引用 deleteItem() JavaScript 函数的 load 命令。这是最后一个需要修改的地方。现在我们来运行一下修改后的示例。


运行修改后的示例

简单地将示例加载到浏览器中。显示的内容与 图 1 中原来显示的一样。但是,当您删除至最后一行并单击 Delete Item 后,您应看到图 4 所示的结果。


图 4. 删除最后一行
删除最后一行

现在最后一行没有消失。而是变为默认值。如果单击 Add Item 按钮,您将看见类似图 5 的内容。


图 5. 删除最后一个 Item 后添加 Item
删除最后一个 item 后添加 item

Add Item 按钮仍然有效,没有什么变化。

结束语

您已经了解了如何使用 JavaScript 创建更智能的 delete item 动作。更重要的是,您了解了如何使用 JavaScript 访问和修改 XForms 模型数据,重新计算 XForms 绑定的计算,以及刷新 XForms 视图。您可以想像几种其他方法,使 Add Item 和 Delete Item 动作提供越来越复杂的功能。希望您也能够发现如何使用这些技术改进您自己的基于 XForms 的应用程序。



下载

描述名字大小下载方法
文章示例代码xformsjavascript_source.zip2KBHTTP

关于下载方法的信息


参考资料

学习

获得产品和技术

讨论

关于作者

Michael Galpin 自 1998 年以来一直从事 Web 应用程序专业开发。他是加州圣何塞市易趣公司的软件工程师。他拥有加州理工学院的数学学位。

关于报告滥用的帮助

报告滥用

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


关于报告滥用的帮助

报告滥用

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


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=247986
ArticleTitle=使用 JavaScript 让 XForms 变得更健壮
publish-date=08132007
author1-email=mike.sr@gmail.com
author1-email-cc=ruterbo@us.ibm.com

标签

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

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

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

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

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