使用remote data
stores的数据来源不单单可以从client-side获得(就是从Registry里获得),也可以通过调用远程数据来获得。对于远程数据的加载和处理工作,GXT已经提供了轻巧方便的手段。她支持通过HTTP协议来检索XML或者JSON格式的数据,或者直接通过GWT RPC来检索到objects对象。对于不同的数据源,GXT提供了相应的机制。如果有必要的话,会把原始的数据转换成ModelData并且自动的放入store里。
对于远程数据的处理过程,会涉及到几个components(非可视化组件)。他们各尽其责,协同工作完成远程数据的检索和加工等操作。
- DataProxy—负责从数据源检索出原始的数据
- DataReader—将检索出的原始数据转换成client-side可用的ModelData
- Loader—将转换好的ModelData自动的加载到store里,给data-backed components使用
这个几个components的相互作用,协同工作图解如下:
DataProxy 接口
DataProxy就是用来从数据源检索原始数据的。根据不同的协议类型,有具体的proxy代理实现类,但他们是都是实现了GXT提供的DataProxy接口的。下面就大致介绍一下具体的实现类
DataProxy | 描述 |
HttpProxy | 通过GWT的Requestbuilder传输同一个server的数据,读取XML或JSON格式的数据。 |
MemoryProxy | 简单的通过指定的构造函数传递数据 |
PagingModelMemoryProxy | 类似MemoryProxy,但是支持在从memory中读取数据时分页 |
RpcProxy | 使用GWT RPC来检索数据,允许其过程中通过loader将javabean数据转换 |
ScriptTagProxy | 通过一个URL来检索其返回的数据,其URL可以是别的域名,而不是自己服务器运行的域名。但是只支持JSON |
DataReader 接口
DataReader的作用是用来翻译原始数据,将其转换成ModelData 类型的object。针对不用类型的原始数据,有对应的具体Reader,但是他们都是实现GXT提供的DataReader接口。一个DataReader的返回值可能是下面列表当中的一个:- ModelData类型的对象集合
- 一个实现了ListLoadResult接口的object——在ListLoadResult接口里有个一getData()方法用来返回ModelData object的
- 一个PagingLoadResult object——PagingLoadResult 扩展了ListLoadResult接口,加入了分页的功能,因此返回一组数据的子集。
下面是一组列表,介绍不同类型的具体的DataReader实现类
DataReader | 输入数据类型 | 转换时,使用的工具类 | 输入数据类型 | 何时使用此Reader |
ModelReader | ModelData | 输入的已经是ModelData,不需要何人转换,只要放入到ListLoadResult即可 | ListLoadResult | 当加载的原始数据已经继承BeanModel |
BeanModelReader | JavaBean的集合 | BeanModelFactory | ListLoadResult | 当加载的原始数据是普通的javabean,需要使用他将其转换成BeanModel |
JsonReader | JSON数据 | ModelType定义 | ModelData结果集 | 当原始数据是Json类型时 |
JsonLoadResult Reader | JSON数据 | ModelType定义 | ListLoadResult | 当原始数据是Json类型时 |
JsonPagingLoadResult Reader | JSON数据 | ModelType定义 | PagingLoadResult | 当原始数据是Json类型时 |
XmlReader | XML数据 | ModelType定义 | ModelData结果集 | 当原始数据是xml类型时 |
XmlLoadResultReader | XML数据 | ModelType定义 | ListLoadResult | 当原始数据是xml类型时 |
XmlPagingLoadResult Reader | XML数据 | ModelType定义 | PagingLoadResult | 当原始数据是xml类型时 |
上面的列表大家会注意到“ModelType定义”这句话。ModelType是在Reader在进行转换的时候会用到的类。
ModelType的使用
ModelType是用来定义原始数据的结构类型的。定要其原始数据的结构之后,reader就会根据此结构进行读取转换。拿XML类型的数据来说,其结构比如:
<books>
<book>
<title>The best book in the world</title>
</book>
<book>
<title>The worst book in the world</title>
</book>
</books>
那么就上面的结构,ModelType应该有如下定义:
final ModelType modelType = new ModelType();
modelType.setRoot("books");
modelType.setRecordName("book");
modelType.addField("title");
跟节点是books,book是记录集,title是具体的一个field属性。
ModelType当然也支持定义Json数据类型的结构。如下的Json数据,ModelType的定义同上
{
"books": [
{
"book": {
"title": "The best book in the world"
},
"book": {
"title": "The worst book in the world"
}
}
]
}
Loader接口
- 当DataProxy将原始数据获得,
- 数据被DataReader转换成ModelData之后
- 就需要使用Loaders,将ModelData装入Store
Loader是所有接口根接口,与之对应的BaseLoader是所有抽象类的根抽象类。当然BaseLoader实现了Loader接口。请看下图:
Loader一共就分两类:一个是List一个是Tree。打开源码一看便知之前的关系。
Loaders可以在数据加载的时候,将其排序,可以功过setSortField,setSortDir方法进行设置,当然也可以通过LoadConfig对象来设置。
LoadConfig
LoadConfig配置了数据是如何被Loader到store的。Loadconfig接口有一系列的实现类(BaseGroupingLoadConfigBasePagingLoadConfig),看名称就猜到大概意思这里就不详细介绍了。我自己也没怎么研究。。。
重新屡屡思路
- GXT所能使用的classes必须要直接或间接的实现了ModelData接口,才可以放入Store里
- Store里面存储的ModelData,其实是充当的client-side的缓存层,Data-backed components直接使用的是Store
- 用DataProxy将远程的原始数据获得
- ModelType定义了远程数据的结构体
- DataReader根据ModelType的描述,把原始的数据转换成ModelData
- Loader按照DataProxy和DataReader的定义,一气呵成,执行之后将数据装入Store。
- Loadconfig的定义告诉Loader对ModelData数据如何排序,分组,分页等配置。
将上面提到的所有内容应用到RSSReader项目里
- 编辑com.danielvaughan.rssreader.client.listsFeedList类的onRender方法
package com.danielvaughan.rssreader.client.lists;
import java.util.List;
import com.danielvaughan.rssreader.client.RSSReaderConstants;
import com.danielvaughan.rssreader.client.services.FeedServiceAsync;
import com.danielvaughan.rssreader.shared.model.Feed;
import com.extjs.gxt.ui.client.Registry;
import com.extjs.gxt.ui.client.data.BaseListLoader;
import com.extjs.gxt.ui.client.data.BeanModel;
import com.extjs.gxt.ui.client.data.BeanModelReader;
import com.extjs.gxt.ui.client.data.ListLoadResult;
import com.extjs.gxt.ui.client.data.ListLoader;
import com.extjs.gxt.ui.client.data.RpcProxy;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.form.ListField;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.rpc.AsyncCallback;
public class FeedList extends LayoutContainer {
public FeedList() {
setLayout(new FitLayout());
}
@Override
protected void onRender(Element parent, int index) {
super.onRender(parent, index);
final ListField<BeanModel> feedList = new ListField<BeanModel>();
//0:从Registry里获得Service
final FeedServiceAsync feedService = (FeedServiceAsync) Registry
.get(RSSReaderConstants.FEED_SERVICE);
//1:定义proxy在load方法里嗲用Serivce里的方法
RpcProxy<List<Feed>> proxy = new RpcProxy<List<Feed>>() {
@Override
protected void load(Object loadConfig,
AsyncCallback<List<Feed>> callback) {
feedService.loadFeedList(callback);
}
};
//2:定义Reader
BeanModelReader reader = new BeanModelReader();
//3:将proxy和reader传入,定义loader
ListLoader<ListLoadResult<BeanModel>> loader = new BaseListLoader<ListLoadResult<BeanModel>>(
proxy, reader);
//4:传入loader,生成store,此时还没有load数据
ListStore<BeanModel> feedStore = new ListStore<BeanModel>(loader);
//5:将stroe绑定到data-backed component身上
feedList.setStore(feedStore);
feedList.setDisplayField("title");
//6:真正的load数据,load成功之后,data-backed component会自动的显示出来。
loader.load();
add(feedList);
}
}