Sys.UI.Data.DataNavigator与Sys.UI.Data.SortBehavior

本文涉及的产品
可视分析地图(DataV-Atlas),3 个项目,100M 存储空间
简介:
  这次我想讨论与分析的内容是 Sys.UI.Data.DataNavigator与Sys.UI.Data.SortBehavior。在这之前,可能感兴趣的朋友们需要先了解一下 Atlas Client Script中Data Control的使用方法。可以先学习一些别的文章,例如官方说明《 Start > Quickstart Developer Tasks > Performing Data Access》,而Dflying兄的 Atlas介绍系列也实属精品。

Sys.UI.Data.DataNavigator和Sys.UI.Data.SortBehavior的作用都是配合DataView进行数据操 作:一个操作分页功能,一个操作排序功能。事实上这两个类都是属于辅助类,它们的功能是更加方便了开发,而提供的额外功能却不多。

Sys.UI.Data.SortBehavior使用了DataView的自带的属性与方法为DataView的内容进行了排序。当一个 SortBehavior被应用到了一个控件时,它会根据控件的点击来决定DataView的排序顺序(在ASC和SESC之间切换),并且根据当前的排 序顺序为control应用不同的样式(sortAscendingCssClass或sortDescendingCssClass)。如果没有这个控 件,我们可能就需要配合使用setProperty,invokeMethod进行较为复杂的应用了。换个角度说,如果某个功能比较复杂并且需要复用的 话,我们可以像SortBehavior一样封装起一系列操作。

Atlas中的控件设计并不随意,而是实实在在地“有用”。

但是,与Sys.UI.Data.SortBehavior相比,Sys.UI.Data.DataNavigator就有些古怪了。

Sys.UI.Data.DataNavigator提供了DataView的分页功能,不过无论是上一页也好,最后一页也罢,它的实现其实只是简单的 使用了DataView的一个函数:set_pageIndex。配合get_pageIndex获得当前的页数,实现这些功能自不必说。不过非常有趣 (或者继续使用上面的说法:古怪)的是触发DataNavigator那些功能的方式。DataNavigator没有提供任何的方法来触发它的那些操 作,它的那些操作都是定义在DataNavigator的onBubbleEvent方法中。它的onBubbleEvent里判断了 Sys.UI.Button对象传给它的Sys.UI.CommandEventArgs对象中commandName属性的值,然后进行不同的操作。

onBubbleEvent是我觉得在Atlas中比较奇特的玩意儿之一。它是定义在Sys.UI.Control类中的虚函数,直接或间接继承 Sys.UI.Control的类可以对它进行重载。不过在代码中并不会调用onBubbleEvent,如果需要bubble event,那么就需要调用定义在Sys.UI.Control类中的raiseBubbleEvent方法。熟悉ASP.NET Server Control开发的朋友们应该可以发现这和ASP.NET中System.Web.UI.Control的OnBubbleEvent和 RaiseBubbleEvent非常的类似,只可惜在Javascript中无法定义protected方法,只得把它们暴露在外了。

可以看出Atlas有多么深厚的ASP.NET背景。与ASP.NET一样,Atlas中的raiseBubbleEvent方法也不是个虚函数,它的定义无法改变,我们来看一下它的代码:
this .raiseBubbleEvent  =   function (source, args) {
    
var  currentTarget  =   this .get_parent();
    
while  (currentTarget) {
        
if  (currentTarget.onBubbleEvent(source, args)) {
            
return ;
        }
        currentTarget 
=  currentTarget.get_parent();
    }
}
  从当前控件开始,调用onBubbleEvent方法,如果返回false,则继续向parent传递,这样event就被bubble了。这是很标准的raiseBubbleEvent功能实现。

但是事情没有这么简单。在raiseBubbleEvent中可以看出,代码是通过parent属性获得上一级控件的引用。parent也是个无法重载 的非虚成员,它定义在Sys.UI.Control中。不知道为什么,在官方的Client Library中缺少这个属性。我们来看一下它的实现。
this .get_parent  =   function () {
    
if  (_parent) {
        
return  _parent;
    }
    
else  {
        
var  parentElement  =   this .element.parentNode;
        
while  (parentElement) {
            
if  (parentElement.control) {
                
return  parentElement.control;
            }
            parentElement 
=  parentElement.parentNode;
        }
        
return   null ;
    }
}
  可以看出这个属性的逻辑,如果曾经定义了 parent,那么就返回parent引用,如果没有的话,那么就沿着DOM向父级元素找,找到第一个具有Atlas控件对象的元素。因此可以发现, parent并不需要被强制定义。为了证实这一点,在这里我借用一下Dflying兄在其文章《 使用ASP.NET Atlas PageNavigator控件实现客户端分页导航》中编写的例子,我将对其进行简单的修改。

在Dflying兄的例子里有如下的定义:
<!--  PageNavigator  -->
< div  id ="pageNavigator" >
    
< input  type ="button"  id ="btnFirstPage"  value ="&lt;&lt;"   />
    
< input  type ="button"  id ="btnPrevPage"  value ="&lt;"   />
    
< span  id ="lblPageNumber" ></ span >  /  < span  id ="lblPageCount" ></ span >
    
< input  type ="button"  id ="btnNextPage"  value ="&gt;"   />
    
< input  type ="button"  id ="btnLastPage"  value ="&gt;&gt;"   />
</ div >

< script  type ="text/xml-script" >
    
< page xmlns:script = " [url]http://schemas.microsoft.com/xml-script/2005[/url] " >
        
< components >
            ...
            
< dataNavigator id = " pageNavigator "  dataView = " view "   />
            
< button id = " btnFirstPage "  parent = " pageNavigator "  command = " FirstPage "   />
            
< button id = " btnPrevPage "  parent = " pageNavigator "  command = " PreviousPage " >
                ...
            
</ button >
            
< button id = " btnNextPage "  parent = " pageNavigator "  command = " NextPage " >
                ...
            
</ button >
            
< button id = " btnLastPage "  parent = " pageNavigator "  command = " LastPage "   />
            ...
        
</ components >
    
</ page >
</ script >
  在这里,dataNavigator是那些 <input type="button" />的父结点,而在Atlas Xml Scripts中也为每个button指定了自身的parent属性。我们先尝试着去除上方HTML定义的元素父子关系,保留下方Script不变:
< div  id ="pageNavigator" ></div>
    
< input  type ="button"  id ="btnFirstPage"  value ="&lt;&lt;"   />
    
< input  type ="button"  id ="btnPrevPage"  value ="&lt;"   />
    
< span  id ="lblPageNumber" ></ span >  /  < span  id ="lblPageCount" ></ span >
    
< input  type ="button"  id ="btnNextPage"  value ="&gt;"   />
    
< input  type ="button"  id ="btnLastPage"  value ="&gt;&gt;"   />
</ div >
  运行,一切正常。可以发现作为dataNavigator的元素无需为那些button的父结点。

接着,我们保留HTML元素的父子关系,在下方撤销在Atlas Scripts中的parent定义,如下:
< script  type ="text/xml-script" >
    
< page xmlns:script = " [url]http://schemas.microsoft.com/xml-script/2005[/url] " >
        
< components >
            ...
            
< dataNavigator id = " pageNavigator "  dataView = " view "   />
            
< button id = " btnFirstPage "  parent = " pageNavigator "  command = " FirstPage "   />
            
< button id = " btnPrevPage "  parent = " pageNavigator "  command = " PreviousPage " >
                ...
            
</ button >
            
< button id = " btnNextPage "  parent = " pageNavigator "  command = " NextPage " >
                ...
            
</ button >
            
< button id = " btnLastPage "  parent = " pageNavigator "  command = " LastPage "   />
            ...
        
</ components >
    
</ page >
</ script >
  运行,依旧一切正常。可见,Atlas会自动在DOM树中寻找作为dataNavigator的control。感兴趣的朋友也可以尝试着将上述两者都去除,这样分页功能就会失效了。

不过,我的疑惑也就出现了……既然这样,为什么DataNavigator要设计成为一个控件?很明显,它只是提供了一个分页功能,而不具备任何显示作 用!我们可以在任意一个地方定义任意一个元素作为所谓的“DataNavigator”,还是能够实现分页。从这一点上说,我们完全可以定义个类似于 DataNavBehavior的东西来实现相同的效果。还有,为什么要使用bubble event的方式来作为实现DataNavigator的功能呢?纵观所有的Atlas控件,只有button一个类调用了 raiseBubbleEvent方法,而在PageNavigator中直接把args参数作为CommandEventArgs对象来处理,这表明它 已经作为一个特定实现而出现了。

有时候我想,DataNavigator的这个设计,莫非只是为了“演示”一下 raiseBubbleEvent和onBubbleEvent的方式?事实上,DataNavigator只是集中了分页的功能,通过一个独立的对象来 管理了操作,照这样说,我们可以为它提供相关方法或属性,然后依靠Button来invokeMethod或者setProperty。另外,熟悉 ASP.NET Server Control开发的朋友们可以知道,在ASP.NET中RaiseBubbleEvent和OnBubbleEvent的作用是为了配合 PostBack模型,在复合控件中将子控件的事件向父控件传递的一种方式。而在Client端,根本不需要这样的做法,所有的控件都是顶级元素,都能被 直接访问并且注册事件,这样raiseBubbleEvent和onBubbleEvent方法似乎也就没有什么必要性了。

目前DataNavigator的设计,似乎只有在资源消耗上小一些。

不过现在,在Client端存在raiseBubbleEvent和onBubbleEvent的必要性,它们能否带来开发概念和方式上的改变,以及如何正确地使用它们,这些事情还在我脑子里激烈地进行着斗争,可能我以后会单独写一篇文章来仔细讨论这点。

似乎这片文章已经完成使命了,不过,事实上,在这里我还想提一下DataNavigator的一个bug。DataNavigator的部分代码如下:
 1  Sys.UI.Data.DataNavigator  =   function (associatedElement) {
 2      Sys.UI.Data.DataNavigator.initializeBase( this , [associatedElement]);
 3      
 4      ...
 5 
 6       this .getDescriptor  =   function () {
 7           var  td  =  Sys.UI.Data.DataControl.callBaseMethod( this , 'getDescriptor');
 8          
 9          td.addProperty('dataView', Object);
10          
11           return  td;
12      }
13      Sys.UI.Data.DataNavigator.registerBaseMethod( this , 'getDescriptor');
14 
15       this .onBubbleEvent  =   function (source, args) {
16          ...
17      }
18      Sys.UI.Control.registerBaseMethod( this , 'onBubbleEvent');
19  }
20  Sys.UI.Data.DataNavigator.registerClass('Sys.UI.Data.DataNavigator', Sys.UI.Control);

  看看第5行和第18行的代码,是不是觉得很奇怪?还好,由于DataNavigator是继承了Sys.UI.Control类,因此在运行效果上没有什么问题。幸甚!


本文转自 jeffz 51CTO博客,原文链接:http://blog.51cto.com/jeffz/60948,如需转载请自行联系原作者

相关实践学习
DataV Board用户界面概览
本实验带领用户熟悉DataV Board这款可视化产品的用户界面
阿里云实时数仓实战 - 项目介绍及架构设计
课程简介 1)学习搭建一个数据仓库的过程,理解数据在整个数仓架构的从采集、存储、计算、输出、展示的整个业务流程。 2)整个数仓体系完全搭建在阿里云架构上,理解并学会运用各个服务组件,了解各个组件之间如何配合联动。 3&nbsp;)前置知识要求 &nbsp; 课程大纲 第一章&nbsp;了解数据仓库概念 初步了解数据仓库是干什么的 第二章&nbsp;按照企业开发的标准去搭建一个数据仓库 数据仓库的需求是什么 架构 怎么选型怎么购买服务器 第三章&nbsp;数据生成模块 用户形成数据的一个准备 按照企业的标准,准备了十一张用户行为表 方便使用 第四章&nbsp;采集模块的搭建 购买阿里云服务器 安装 JDK 安装 Flume 第五章&nbsp;用户行为数据仓库 严格按照企业的标准开发 第六章&nbsp;搭建业务数仓理论基础和对表的分类同步 第七章&nbsp;业务数仓的搭建&nbsp; 业务行为数仓效果图&nbsp;&nbsp;
相关文章
system.dll,Nskhelper2.sys,oapejg.sys,991b0345.dat,NsPass0.sys等1
system.dll,Nskhelper2.sys,oapejg.sys,991b0345.dat,NsPass0.sys等1
|
5月前
|
C++ Windows
vs2019 This application failed to start because it could not find or load the QT platform plugin
这篇文章介绍了在VS2019中解决QT程序运行时出现的“无法找到或加载QT平台插件”错误的步骤,通过将必要的DLL文件和插件目录复制到项目解决方案中解决了问题。
|
8月前
How to drop and recreate SYS.SYS$SERVICE_METRICS_TAB
How to drop and recreate SYS.SYS$SERVICE_METRICS_TAB
52 1
|
7月前
|
Windows
Qtdesigner报错:This application failed to stat could not find or load the Qt platform plugin “windows“
Qtdesigner报错:This application failed to stat could not find or load the Qt platform plugin “windows“
UE Select File / Folder Window 插件说明
UE Select File / Folder Window 插件说明
182 0
Qt:ui文件no member named actionXXX 解决方案
Qt:ui文件no member named actionXXX 解决方案
748 0
SAP WM 为Storage Type 004激活SUM报错 - Storage types without pick-point stor.type require partial pallet
SAP WM 为Storage Type 004激活SUM报错 - Storage types without pick-point stor.type require partial pallet
SAP WM 为Storage Type 004激活SUM报错 - Storage types without pick-point stor.type require partial pallet
|
数据库管理
Xamarin.Android 使用 SQLite 出现 Couldn't read row 0, col -1 from CursorWindow. 异常
异常:Java.Lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow.  Make sure the Cursor is initialized correctly before accessing data from it. 出现该问题一般分为两种情况: 1.请求的字段不在表中,可能是大小写没写对。
6580 0