Dojo中的事件处理
根据Jeff Atwood的建议,我决定将JavaScript库用于我正在编写的最基本的待办事项列表应用程序。我选择了Dojo工具箱,版本1.1.1. 最初,一切都很好:我编写的拖放代码是第一次工作,我们可以在屏幕上拖动任务以更改其优先顺序,并且每个拖放操作都调用一个事件处理程序,该事件处理程序发送一个AJAX调用服务器以告知其订单已更改。
然后,我添加了电子邮件跟踪功能。标准内容:新收到的电子邮件在其主题行上添加了一个唯一的ID号,有关该问题的所有后续电子邮件都可以通过在我们回复时将ID号保留在主题中来进行跟踪。因此,我们有一个打开的任务列表,每个任务都有自己的ID号,并且每个任务都有一个按时间顺序排列的关联电子邮件列表。我希望这些电子邮件的文本在用户查看任务列表时可供用户使用,因此我将每个任务框设置为Dijit" Tree"控件的顶层,其中包含任务描述,分支包含电子邮件日期,并且单个每个分支的"叶"包含电子邮件文本。
第一个问题:默认情况下,我希望树视图完全折叠。在对Google进行了广泛的搜索之后,我发现了许多解决方案,所有这些解决方案似乎都对早期版本的Dojo有效,但不适用于我正在使用的解决方案。我最终发现,最好的解决方案似乎是在Tree控件加载后调用事件处理程序,该事件处理程序仅折叠每个分支/叶子。不幸的是,即使已经实例化Tree控件并调用了其"启动"事件处理程序,分支和叶子仍未加载(数据仍通过AJAX调用加载)。因此,我修改了系统,以便在服务器端添加所有电子邮件文本和Tree结构。这意味着在调用其启动事件处理程序时,整个完全填充的Tree控件将可用。
因此,启动事件处理程序将树完全折叠。接下来,我找不到一种"适当"的方式来为电子邮件叶子提供漂亮的格式化文本。我可以将电子邮件文本放到叶子中,但是任何HTML都会被逸出并显示在网页中。可能会在Dojo的文档(往往过时,带有1.0之前的版本的代码和示例)周围翻腾。我最终想出了一种解决方案,使JavaScript能够运行并读取每个叶节点内部的SPAN元素,并在转义的innerHTML中取消转义的HTML代码。我认为我会在Tree控件的启动事件处理程序中将代码与完全折叠的树代码一起使用。
但是,事实证明,直到用户单击expando(在我们单击以展开节点的树视图中的小" +"符号)之前,实际上不会创建SPAN元素。好吧,很公平,我将重新格式化的代码添加到onExpand()事件处理程序中,或者将其称为任何内容。似乎不存在。我已经搜索了文档,也已经搜索了Google ...我很可能误解了Dojo的"发布/订阅"事件处理系统,但是我认为主要是因为似乎没有针对该文档的全面文档它在任何地方(例如,我在哪里可以找到可以订阅的事件?)。
因此,最后,我能想到的最好的解决方案是将onClick事件处理程序(不是" Dojo"事件,而是Dojo一无所知的普通JavaScript事件)添加到每个在每个叶子的SPAN元素内对HTML进行格式设置。除非...被调用,否则SPAN元素仍然不存在(有时会被缓存,以进一步使我们感到困惑)。因此,我让事件处理程序设置了一个计时器,该计时器定期调用一个函数,该函数在重新格式化之前检查相关SPAN元素是否已打开。
// An event handler called whenever a "email title" tree node is expanded. function formatTreeNode(nodeID) { if (dijit.byId(nodeID).getChildren().length != 0) { clearInterval(nodeUpdateIntervalID); messageBody = dijit.byId(nodeID).getChildren()[0].labelNode.innerHTML if (messageBody.indexOf("<b>Message text:</b>") == -1) { messageBody = messageBody.replace(/>/g, ">"); messageBody = messageBody.replace(/</g, "<"); messageBody = messageBody.replace(/&/g, "&"); dijit.byId(nodeID).getChildren()[0].labelNode.innerHTML = "<b>Message text:</b><div style=\"font-family:courier\">"+messageBody+"</div>"; } } } // An event handler called when a tree node has been set up - we changed the default fully-expanded to fully-collapsed. function setupTree(theTree) { dijit.byId("tree-"+theTree).rootNode.collapse(); messageNode = dijit.byId("tree-"+theTree).rootNode.getChildren(); for (pl = 0; pl < messageNode.length; pl++) { messageNode[pl].collapse(); messageNode[pl].expandoNode.onclick = eval("nodeUpdateIntervalID = setInterval(\"formatTreeNode('"+messageNode[pl].id+"')\",200); formatTreeNode('"+messageNode[pl].id+"');"); } }
上面的内容确实有种骇人听闻的感觉,而且我确信在思考过程的早期,我一定在某个地方走错了路。有人可以告诉我:
- 将格式正确的文本放入Dojo / Dijit Tree控件中的正确方法。
- 处理Dojo事件的正确方法,例如我可以确定哪些事件可供我订阅。
- 更好的JavaScript库可以使用(我可以使用JQuery来做我想做的事,而避免上面看到的全方位的方法吗?)。
PS:如果我们要命名一个软件项目,请考虑一下其名称在Google中的唯一性。我敢肯定,在所有武术成果都不会妨碍的情况下,在Google中搜索" Dojo"文档会更加容易。
PPS:Firefox拼写检查器知道如何拼写" Atwood",当我用两个" T"代替一个" T"时会纠正我。杰夫现在那么出名吗?
解决方案
回答
我假设我们遵循Dojo 1.1教程中的dijit.Tree和dojo.data,该教程指示我们使用数据存储将数据传递给树控件。那让我敲了一下砖墙的头一会儿。
这不是一个很好的方法,替代方法也没有得到很好的记录。我们需要创建一个使用模型。我在下面提供了一个树模型的示例,该树模型是我创建的,用于显示LDAP目录的结构。
我们可以在./dijit/_tree/model.js的dojo发行版中找到该模型的默认实现。注释应有助于我们理解模型支持的功能。
IDirectoryService类的下面代码是直接Web远程处理(DWR)生成的服务器端Java POJO的存根。如果我们要进行很多客户端-服务器交互,我强烈建议DWR。
dojo.declare("LDAPDirectoryTreeModel", [ dijit.tree.model ], { getRoot : function(onItem) { IDirectoryService.getRoots( function(roots) { onItem(roots[0]) }); }, mayHaveChildren : function(item) { return true; }, getChildren : function(parentItem, onComplete) { IDirectoryService.getChildrenImpl(parentItem, onComplete); }, getIdentity : function(item) { return item.dn; }, getLabel : function(item) { return item.rdn; } });
这是我的JSP页面的摘录,我在其中创建了模型并使用它来填充树控件。
<div dojoType="LDAPDirectoryTreeModel" jsid="treeModel" id="treeModel"> </div> <div jsid="tree" id="tree" dojoType="dijit.Tree" model="treeModel" labelAttr="name" label="${directory.host}:${directory.port}"> </div>