天天看点

ExtJs之数据和ComboBox控件

一、如何获取数据

在ExtJs中获取数据主要靠下面四个类: Ext.data.DataProxy、Ext.data.Record、Ext.data.DataReader、Store。下面对这三个类做个简单的介绍。

DataProxy:获取想要的数据。通过他能得到来自不同地方的数据,如数组、远程服务器,并组织成不同的格式。

DataReader:定义数据项的逻辑结构,一个数据项有很多列,每列的名称是什么,分别是什么数据类型,都由该类来定义。另外,还负责对不同格式的数据进行读取和解析。在这里用到了Ext.data.Record类来定义具体的列类型。

Store:存储器,用于整合Proxy和Reader,控件索取数据时通常和他打交道。

二、Ext.data.DataProxy 类

proxy是代理的意思,很多时候,聪明的架构师和设计者为了屏蔽底层的差异,给用户提供一个统一的访问接口,会设计一个名为“proxy”的类。这种设计哲学在架构中普通应用,并且解决了很多实际问题,代码也变得更加优雅,并有效降低代码侵入。

Ext.data. DataProxy就是来源于这样一种灵感。

Ext.data.DataProxy 是获取数据的代理,数据可能来自于内存,可能来自于同一域的远程服务器数据,更有可能来自于不同域的远程服务器数据。

但是,在实际应用中,我们不会直接使用Ext.data.DataProxy,而是使用他的子类:MemoryProxy、HttpProxy和ScriptTagProxy,他们的作用分别是:

MemoryProxy:获取来自内存的数据,可以是数组、json或者xml。

HttpProxy:使用HTTP协议通过ajax 从远程服务器获取数据的代理,需要指定url。

ScriptTagProxy:功能和HttpProxy一样,但支持跨域获取数据,只是实现时有点偷鸡摸狗。

因为数据最终要显示在ComboBox 中,所以我们以ComboBox 为中心进行设计。大家应该知道,下拉列表框有两个值,一个是显示值,另一个是实际值,也就是说,一个数据项包含两列:显示值列和实际值列。我们定义一个用来保存城市名称的二维数组:

var cities = [["1", "沈阳"], ["2", "大连"], ["3", "鞍山"], ["4", "辽阳"]];      

在二维数组中,每一个城市保存了两个值:值一表示城市编号,作为实际值,值二表示城市名称,作为显示值。然后,我们将data 构建出一个MemoryProxy对象:

var mp = new Ext.data.MemoryProxy(cities);      

三、Ext.data.DataReader 类

DataReader 从来不是单独行动的,他没有太多的自主权,总是看DataProxy行事。总体来说,DataReader 用来定义数据项(行)的逻辑结构,主要信息有:列的逻辑名称(name)、列的数据类型(type)、列与数据源的索引映射(mapping)等,另外,还包含一些元数据,如分页信息。

实际上,每一个数据项都是一个Ext.data.Record(记录)对象,而数据项的列信息则是通过Ext.data.Record来定义的。Ext.data.Record并没有固定的结构,他保存的是一个json对象数组,数组的元素个数由列的数量来决定。在上面的例子中,城市包含ID 和名称,所以,必须在数组中定义两个元素,基本就是这个样子:

var myRecord = Ext.data.Record.create([{
				name : 'cid',
				type : 'int',
				mapping : 0
			}, {
				name : 'cname',
				type : 'string',
				mapping : 1
			}]);      

我们定义了一个myRecord的结构,通过Ext.data.Record.create创建,参数是一个json对象数组,name 和type 分别表示每一列的名称和数据类型,mapping 是列值与数组元素的映射关系。 

Record 创建好后,必须和DataReader 关联,DataReader 也同样有三个子类:Ext.data.ArrayReader、Ext.data.JsonReader、Ext.data.XmlReader。我之前说过DataReader 从来不单独行动,使用哪一个子类主要取决于DataProxy中封装的数据类型,如果是数组,则使用Ext.data.ArrayReader;如果是json,则使用Ext.data.JsonReader;如果是xml,则使用Ext.data.XmlReader。

在本例中,我们处理的数据类型是数组,所以自然要使用ArrayReader。

var myReader = new Ext.data.ArrayReader({}, myRecord);      

构造ArrayReader 对象时,构造函数的第一个参数就是元数据,第二个参数则是Record。也可以一步到位: 

var myReader = new Ext.data.ArrayReader({}, [
{name: "cid", type: "int", mapping: 0},
{name: "cname", type: "string", mapping: 1}
]);      

四、Ext.data.Store 类

这个类相对简单,不需要面对数据和结构,只是把DataProxy和DataReader 整合在一起,这样一来,数据有了,结构有了,俨然就是一张数据表,想一想数据库中的物理表是不是就是这样的呢?嗯,非常像。

典型的写法像这样:

var store = new Ext.data.Store({
    proxy: proxy,
    reader: reader
});      

到了这一步,别以为大功告成了,其实这时候数据并没有加载到Store中,默认情况下,Store采取延时加载,必须显式调用load()方法,当然,我们也可以采取即时加载策略,按如下配置即可: 

var store = new Ext.data.Store({
    proxy: proxy,
    reader: reader,
    autoLoad: true //即时加载数据
});      

我们画张图,用来描述这三个类之间的关系。

ExtJs之数据和ComboBox控件

可以看出,Ext.data.Store的主要目的是在内存中建立一张数据表,填充到组件中,这些组件形态也千差万别,最典型的就是ComboBox 和GridPanel。

五、下拉列表框

下拉列表框被定义成ComboBox 类,位于Ext.form命名空间,和Button不同,他是一个表单域组件,常用于表单中。

我们有一个这样的ComboBox,该组件用来显示辽宁的城市。

ExtJs之数据和ComboBox控件

看起来和select标记别无二致,只是更加漂亮了。  

一起来熟悉一个比较典型的ComboBox的定义方法:

var combobox = new Ext.form.ComboBox({
			renderTo : Ext.getBody(),
			triggerAction : "all",
			store : store,
			displayField : "cname",
			valueField : "cid",
			mode : "local",
			emptyText : "请选择辽宁城市"
		});      

嘿嘿,如果不解释您一定会愣上半天,有时候真不习惯Extjs中属性名称的定义风格,也许只怪我们的头发太长见识太少,来认识一下吧。

triggerAction:是否开启自动查询的功能,为all表示不开启,为query表示开启,默认为query;

store:不用解释了,和前面的Ext.data.Store对象挂勾,定义数据源;

displayField:关联Record的某一个逻辑列名作为显示值,本例为城市名称;

valueField:关联Record的某一个逻辑列名作为实际值,本例为城市ID;

mode:可选值有local和remote,如果数据来自本地,用local,如果数据来自远程服务器,必须用remote,默认为remote;

emptyText:没有选择任何选项的情况文本框中的默认文字。

请理解并记住这个基本的用法,很多东西都是这样巩固消化的。当我们面对如海水般涌来的信息,第一感觉就是无所适从,继而惊惶失措,夺路而逃,于是被永远挡在真理的大门之外。成功和失败,只在一念之间。

抓住核心点之后,慢慢扩展,知识架构便会越来越丰满,因为把握好了整个知识点的精髓,再怎么变化,都不至于迷路。 

六、得到下拉列表框的值

下拉列表框(ComboBox)有两个值,显示值和实际值。显示值为用户提供了更为直观和理想的体验,而实际值则会传送到服务器,供服务器处理。

ComboBox定义了两个方法,其中,getValue()用于返回实际值,getRawValue()用于返回显示值。

我在页面上放置了一个按钮,点击按钮显示下拉列表框的值:

var btn = new Ext.Button({
			text : "列表框的值",
			renderTo : Ext.getBody(),
			handler : function() {
				Ext.Msg.alert("值", "实际值:" + combobox.getValue() + ";显示值:"
								+ combobox.getRawValue());
			}
		});      

七、源代码

下面是本章示例的源程序:

Ext.onReady(function() {
			cmp();
		});

var cmp = function() {
	// 1. 定义本地静态数据
	var cities = [["1", "沈阳"], ["2", "大连"], ["3", "鞍山"], ["4", "辽阳"]];
	// 2. 根据数据定义内存代理
	var mp = new Ext.data.MemoryProxy(cities);
	// 3. 创建记录格式
	var myRecord = Ext.data.Record.create([{
				name : 'cid',
				type : 'int',
				mapping : 0
			}, {
				name : 'cname',
				type : 'string',
				mapping : 1
			}]);
	// 4.把数据按照创建的记录格式进行组合
	var myReader = new Ext.data.ArrayReader(mp, myRecord);
	//var reader = new Ext.data.ArrayReader({}, myRecord);
	
	// 5.把数据按照一定格式组成的数据组传入缓存
	var myStore = new Ext.data.Store({
				proxy : mp,
				reader : myReader,
				autoLoad : true
			});
	// store.load(); 如果在Store配置信息中使用autoLoad:true,就可以不用此方法.
	// 5.创建下拉列表,把数组写入下接列表
	var myCb = new Ext.form.ComboBox({
				id : "myCb1",
				store : myStore,
				title : "辽宁省下属市",
				emptyText : "辽宁省下属市",
				displayField : "cname",
				valueField : "cid",
				triggerAction : "all",
				mode : "local",
				renderTo : Ext.getBody()
			});
	// 6.创建按钮,实现读取下拉列表
	var myBtn = new Ext.Button({
				id : "myBtn1",
				type : "button",
				text : "所选城市",
				renderTo : Ext.getBody(),
				handler : function() {
					if (myCb.getValue() == "") {
						Ext.Msg.alert("提示","请做出选择");
						myCb.focus(true);
					} else {
						Ext.Msg.alert("值", "实际值:" + myCb.getValue() + ";显示值:"
										+ myCb.getRawValue());
					}
				}
			});
}