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

简介:
  这次我想讨论与分析的内容是 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,如需转载请自行联系原作者

相关实践学习
基于Hologres轻松玩转一站式实时仓库
本场景介绍如何利用阿里云MaxCompute、实时计算Flink和交互式分析服务Hologres开发离线、实时数据融合分析的数据大屏应用。
阿里云实时数仓实战 - 项目介绍及架构设计
课程简介 1)学习搭建一个数据仓库的过程,理解数据在整个数仓架构的从采集、存储、计算、输出、展示的整个业务流程。 2)整个数仓体系完全搭建在阿里云架构上,理解并学会运用各个服务组件,了解各个组件之间如何配合联动。 3&nbsp;)前置知识要求 &nbsp; 课程大纲 第一章&nbsp;了解数据仓库概念 初步了解数据仓库是干什么的 第二章&nbsp;按照企业开发的标准去搭建一个数据仓库 数据仓库的需求是什么 架构 怎么选型怎么购买服务器 第三章&nbsp;数据生成模块 用户形成数据的一个准备 按照企业的标准,准备了十一张用户行为表 方便使用 第四章&nbsp;采集模块的搭建 购买阿里云服务器 安装 JDK 安装 Flume 第五章&nbsp;用户行为数据仓库 严格按照企业的标准开发 第六章&nbsp;搭建业务数仓理论基础和对表的分类同步 第七章&nbsp;业务数仓的搭建&nbsp; 业务行为数仓效果图&nbsp;&nbsp;
相关文章
custom field further usage - add into UI and report
custom field further usage - add into UI and report
113 0
custom field further usage - add into UI and report
predefined data types in UI5
PREDEFINED_TYPES sap.ui.base.DataType.getType
predefined data types in UI5
Could not open app - SAP UI5 error message
Created by Wang, Jerry, last modified on Mar 10, 2015
112 0
Could not open app - SAP UI5 error message
handle search in myNote app
Created by Wang, Jerry, last modified on Feb 05, 2015
handle search in myNote app
OPA 10 - sap.ui.test.matchers.PropertyStrictEquals
Created by Wang, Jerry, last modified on Nov 08, 2015
103 0
OPA 10 - sap.ui.test.matchers.PropertyStrictEquals
PREDEFINED_TYPES sap.ui.base.DataType.getType image
PREDEFINED_TYPES sap.ui.base.DataType.getType image
101 0
PREDEFINED_TYPES sap.ui.base.DataType.getType image
ui5 resource file 404 error
Created by Jerry Wang, last modified on Nov 07, 2014
ui5 resource file 404 error
test of ui5 duplicate control id
Created by Jerry Wang, last modified on Aug 21, 2015
101 0
test of ui5 duplicate control id
How does model reference pass from app view to master view
Created by Wang, Jerry, last modified on May 21, 2015
How does model reference pass from app view to master view