数据包是你应用程序加载和保存所有数据的地方。它包含大量的类,其中有三个比其它所有的都更重要。
它们是: Ext.data.Model / Store /Ext.data.proxy.Proxy
这三个类几乎被所有的应用程序使用,并且得到了许多周围类来支持。
Models模型
数据包的核心就是Ext.data.Model.一个模型就在应用程序中代表了一个实体,举个例子,一个电子商务应用程序可能需要有用户、商品和订单,最基本的层次上来说,一个模型就定义了一系列的字段和其相关联的业务逻辑。
我们可以看一下模型的几个主要部分:
Fields/ Proxies/ Validations/Associations
创建一个模型
通常来说,使用一个基类来定义你的模型是一个好的选择。这个基类可以方便你在一个地方对你的模型进行特定的配置,这样也方便配置schema,schema是你应用程序所有模型的管理器,现在我们可以先重点关注下两个比较有用的配置选项:
Ext.define('MyApp.model.Base',{
extend: 'Ext.data.Model',
fields: [{
name: 'id',
type: 'int'
}],
schema: {
namespace: 'MyApp.model', // generate auto entityName
proxy: { // Ext.util.ObjectTemplate
type: 'ajax',
url: '{entityName}.json',
reader: {
type: 'json',
rootProperty:'{entityName:lowercase}'
}
}
}
});
不同的应用程序在基类模型中会有不同的内容,尤其是fields.
Proxies代理
代理是在Models和Stores中使用的,它负责加载和保存模型的数据。有两种不同类型的代理:客户端和服务端(Client And Server).
代理可以直接在基类model的schema中定义,比如上面的代码例子。
客户端代理
客户端的例子有Memory和Local
Storage,使用了HTML5的localstorage特性。尽管旧的浏览器不支持这些新的HTML5 AP,但是它在实际应用中是非常有用的。
服务端代理
服务端代理负责封装远程服务的数据,例子有AJAX/JSONP/REST。
Schema
Schema是一系列有彼此关联的实体。当一个模型配置了schema,所有的衍生类都会继承。上述例子中,schema已经默认给所有的模型配置了两个默认属性。
第一个配置是namespace命名空间,通过指定这个命名空间,所有实体都拥有了一个缩略名,缩略名在定义实体关联关系的时候非常重要。
第二个配置是 代理proxy,这是一个类模板,跟Ext.XTemplete有点类似。不同的就是类模板在给定数据的时候产生的是对象,在此指定之后,没有显示指定proxy的都会使用该proxy.
应用程序中很可能会存在这种微小差异但又绝大多数相同的请求,通过这样配置,可以减少每个模型直接的重复定义proxy。
User.json被我们指定为url:{entityName.json},应该返回JSON字符串。在例子中我们使用的是
{
"success": "true",
"user": [
{
"id": 1,
"name": "Philip J.Fry"
},
{
"id": 2,
"name": "HubertFarnsworth"
},
{
"id": 3,
"name": "TurangaLeela"
},
{
"id": 4,
"name": "Amy Wong"
}
]
}
Stores
通常情况下model模型会配合store一起使用,store是一系列的records记录的集合,而record是一个模型驱动的类的实例,下面的例子是一个创建store和加载数据的:
var store = new Ext.data.Store({
model: 'MyApp.model.User'
});
store.load({
callback:function(){
var first_name =this.first().get('name');
console.log(first_name);
}
});
我们手动加载store来获取一系列的MyApp.model.User的记录,这些记录在加载完成后会在回调事件中打印出来。
内联数据Inline data
Store可以加载内联数据,store根据我们传入的对象加载到模型适合的类型中去。
new Ext.data.Store({
model: 'MyApp.model.User',
data: [{
id: 1,
name: "Philip J. Fry"
},{
id: 2,
name: "Hubert Farnsworth"
},{
id: 3,
name: "Turanga Leela"
},{
id: 4,
name: "Amy Wong"
}]
});
排序和分组
Stores可以执行排序,分组和过滤,可以是本地的也可以是服务端的。
new Ext.data.Store({
model: 'MyApp.model.User',
sorters: ['name','id'],
filters: {
property: 'name',
value : 'Philip J. Fry'
}
});
在这个store中,数据通过name和id排序,数据会过滤只加载包含Philip J. Fry的用户,通过API可以随时对这些属性进行调整修改。
Associations关联关系
模型可以通过关联关系API聚合到一起,多数应用程序会处理很多模型,并且模型直接存在关系。一个授权博客程序可能会有用户和发稿,每一个用户创建很多稿件,所以一个用户可能会存在多个发稿,但是一个稿件只能被一个用户发布,这就是大家熟知的多对一关系,我们在ExtJS中这样表达:
Ext.define('MyApp.model.User',{
extend: 'MyApp.model.Base',
fields: [{
name: 'name',
type: 'string'
}]
});
Ext.define('MyApp.model.Post',{
extend: 'MyApp.model.Base',
fields: [{
name: 'userId',
reference: 'User', // the entityNamefor MyApp.model.User
type: 'int'
}, {
name: 'title',
type: 'string'
}]
});
在应用程序中对丰富的关系模型进行表述可以说是很方便了。每一个模型都可以有任意数量的对其他模型的关联关系。另外,你的模型定义的顺序也可以比较随意,一旦你有了这个模型的类型,你就可以方便的遍历关联数据了。比如,你想获取一个用户所有的发稿,你就可以这样做:
// Loads User with ID 1 andrelated posts and comments
// using User's Proxy
MyApp.model.User.load(1, {
callback: function(user) {
console.log('User: ' +user.get('name'));
user.posts(function(posts){
posts.each(function(post) {
console.log('Post: ' +post.get('title'));
});
});
}
});
上述代码是说,model新增了一个方法user.posts(),每一个用户都有许多发稿,调用user.posts()就会返回Post模型配置的store。
关联关系不仅在加载数据的时候有用,在创建新的记录的时候也可以用:
user.posts().add({
userId: 1,
title: 'Post 10'
});
user.posts().sync();
这个实例创建了一个新的发稿,自动赋值用户ID给ID字段,调用sync()方法通过proxy保存新的发稿,这是异步的方法,所以你可以设置当完成操作的时候进行回调。
逆关联关系在Post模型中也会产生新的方法:
MyApp.model.Post.load(1, {
callback: function(post) {
post.getUser(function(user) {
console.log('Got user from post: '+ user.get('name'));
});
}
});
MyApp.model.Post.load(2, {
callback: function(post) {
post.setUser(100);
}
});
加载方法getUser()是异步的,需要一个回调来创建用户实例,setUser()方法仅仅就是更新了用户id为100并且保存了Post模型,跟以前一样,可以通过保存操作的返回成功与否触发回调。
加载嵌入数据
当关联关系定义好之后,单请求可以同时请求到关联数据,比如,看下面的返回值;{
"success": true,
"user": [{
"id": 1,
"name": "Philip J.Fry",
"posts": [{
"title": "Post1"
},{
"title": "Post2"
},{
"title": "Post3"
}]
}]
}
通过单请求,可以同时获取用户和用户下的文章,儿不必先请求用户,再根据用户获取下面的发稿。
验证
模型同时也提供了对数据校验的一大群支持,为了进行演示,我们通过上述例子进行构造数据,我们给用户模型增加了一些数据校验:
Ext.define('MyApp.model.User',{
extend: 'Ext.data.Model',
fields: ...,
validators: {
name: [
'presence',
{ type: 'length', min: 7 },
{ type: 'exclusion', list: ['Bender']}
]
}
});
校验定义一个name字段,对该字段进行校验,校验规则就是name字段下配置的对象,例子说的是对name字段进行校验,校验规则是长度不得少于7个字符,数据可以是除了Bender之外的任何数据。
有些严重包含其他一些配置选择,比如长度可以有最长和最短,max min属性,格式化可以使用matcher等,ExtJS提供了五个校验,用户自定义校验也很简单:
Presence-确保该字段必须存在值,不允许有空字符串。
Length-确保字符串有正确的长度,最大最小都是可选的。
Format-确保字符串满足正则表达式的格式。
Inclusion-确保值包含我们要求的。
Exclusion-确保值不包含我们要求的。
下面可以通过用户实例来尝试使用校验,我们创建一个用户,进行校验的:
// now lets try to create anew user with as many validation
// errors as we can
var newUser = newMyApp.model.User({
id: 10,
name: 'Bender'
});
// run some validation on thenew user we just created
console.log('Is User valid?',newUser.isValid());
//returns 'false' as therewere validation errors
var errors =newUser.getValidation(),
error = errors.get('name');
console.log("Error is:" + error);
关键方法就是getValidation(),通过该方法进行校验和返回校验结果,校验记录是懒加载的并且需要的时候才更新。
上面的结果是:Length must be greater than 7
所以我们提供一个大于7个字符的字符:
newUser.set('name', 'BenderBending Rodriguez');
errors =newUser.getValidation();
这样,所有条件都被满足了,调用newUser.isValid()返回true,当我们调用getValidation()就能保证字段都是正常的了。