首页 > 代码库 > 【ExtJS 4.x学习教程】(5)数据包(The Data Package)

【ExtJS 4.x学习教程】(5)数据包(The Data Package)

作者:周邦涛(Timen)
Email:zhoubangtao@gmail.com
转载请注明出处:  http://blog.csdn.net/zhoubangtao/article/details/27707361

1. 简介

数据包主要负责加载和保存你应用程序的所有数据,它包含41个类,但是其中三个是最重要的—— Model,Store和Ext.data.proxy.Proxy。这三个类几乎在每个应用程序都有使用,并且有很多卫星类作支持。


2. Model和Store

data包的核心是Ext.data.Model。一个Model代表了一些数据的类型 —— 例如一个电子商务可能Users,Products和Orders模型。最简单的说,一个Model就是一个字段和数据的集合。现在来看看一个Model类的4个主要部分 —— Field,Proxy,Association和Validation。


让我们看看怎么创建一个Model:

Ext.define('User', {
    extend: 'Ext.data.Model',
    fields: [
        { name: 'id', type: 'int' },
        { name: 'name', type: 'string' }
    ]
});

Model通常和Store一起使用,Store就是Model实例的集合。创建一个Store并且加载它的数据很简单:

Ext.create('Ext.data.Store', {
    model: 'User',
    proxy: {
        type: 'ajax',
        url : 'users.json',
        reader: 'json'
    },
    autoLoad: true
});

我们来给我们的Store配置一个Ajax Proxy,告诉它加载数据url以及解析数据的Reader。这个例子中,我们的服务器返回的是JSON,所以我们需要设置一个Json Reader去读取回复。Store自动从users.json。users.json url应该返回一个像下边的JSON字符串:

{
    success: true,
    users: [
        { id: 1, name: 'Ed' },
        { id: 2, name: 'Tommy' }
    ]
}

3 内部数据

Store也可以从内部加载数据。在内部,Store将每一个传过去的对象转换成Model实例:

Ext.create('Ext.data.Store', {
    model: 'User',
    data: [
        { firstName: 'Ed',    lastName: 'Spencer' },
        { firstName: 'Tommy', lastName: 'Maintz' },
        { firstName: 'Aaron', lastName: 'Conran' },
        { firstName: 'Jamie', lastName: 'Avins' }
    ]
});

4. 排序和分组

Store可以执行排序、过滤和本地分组,以及支持远程排序、过滤和分组:

Ext.create('Ext.data.Store', {
    model: 'User',

    sorters: ['name', 'id'],
    filters: {
        property: 'name',
        value   : 'Ed'
    },
    groupField: 'age',
    groupDir: 'DESC'
});

在我们刚刚创建的Store中,数据会先以name,然后以id排序;并且被过滤到只剩下name包含‘Ed’的用户,并且数据会以age做降序分组。可以通过Store的API在任意时间方便的改变排序、过滤和分组。

5. 代理(Proxy)

Store采用Proxy控制Model数据的加载和保存。有两种类型的Proxy:Client和Server。Client的例子是存储数据到浏览器内存的Memory Proxy和使用HTML5本地存储特性的Local Storage Proxy。Server Proxy处理远程服务的数据解码,它的例子有Ajax Proxy,JsonP Proxy以及Rest Proxy。

Proxy可以直接定义在一个Model中,例如:

Ext.define('User', {
    extend: 'Ext.data.Model',
    fields: ['id', 'name', 'age', 'gender'],
    proxy: {
        type: 'rest',
        url : 'data/users',
        reader: {
            type: 'json',
            root: 'users'
        }
    }
});

// Uses the User Model's Proxy
Ext.create('Ext.data.Store', {
    model: 'User'
});

这有两点好处。第一,每一个使用User Model的Store将会以同样的方式加载它所需要的数据,所以避免了为每一个Store重复定义Proxy。第二,我们能够不通过Store加载和保存Model数据。

data without a Store:

// Gives us a reference to the User class
var User = Ext.ModelMgr.getModel('User');

var ed = Ext.create('User', {
    name: 'Ed Spencer',
    age : 25
});

// We can save Ed directly without having to add him to a Store first because we
// configured a RestProxy this will automatically send a POST request to the url /users
ed.save({
    success: function(ed) {
        console.log("Saved Ed! His ID is "+ ed.getId());
    }
});

// Load User 1 and do something with it (performs a GET request to /users/1)
User.load(1, {
    success: function(user) {
        console.log("Loaded user 1: " + user.get('name'));
    }
});

还有一些使用HTML5新特性的Proxy —— LocalStorage和SessionStorage。尽管较早的浏览器并不支持这些新的HTML5 API,它们是如此的有用,一大部分应用程序将会因它们的存在而受益。

6. 关联

Model能够采用Associations API关联在一起。大多说应用程序处理各种各样的Model,并且这些Model还总是相关联。一个博客应用可能有User、Post和Comment Model。每一个User创建多个Post,每个Post又接受多个Comment。看看我们是怎么用Association来写它们的模型的。

Ext.define('User', {
    extend: 'Ext.data.Model',
    fields: ['id', 'name'],
    proxy: {
        type: 'rest',
        url : 'data/users',
        reader: {
            type: 'json',
            root: 'users'
        }
    },

    hasMany: 'Post' // shorthand for { model: 'Post', name: 'posts' }
});

Ext.define('Post', {
    extend: 'Ext.data.Model',
    fields: ['id', 'user_id', 'title', 'body'],

    proxy: {
        type: 'rest',
        url : 'data/posts',
        reader: {
            type: 'json',
            root: 'posts'
        }
    },
    belongsTo: 'User',
    hasMany: { model: 'Comment', name: 'comments' }
});

Ext.define('Comment', {
    extend: 'Ext.data.Model',
    fields: ['id', 'post_id', 'name', 'message'],

    belongsTo: 'Post'
});

在你的应用中表述不同Model之间的关系很轻松。每一个Model可以包含和其他Model的任意数量的关联关系,并且你的Model可以以任意顺序定义。一旦我们有了一个Model实例,我们就可以通过它访问它关联的数据 —— 例如,如果想把一个给定用户的所有Post的所有Comment打印成日志,我们可以这么做:

// Loads User with ID 1 and related posts and comments using User's Proxy
User.load(1, {
    success: function(user) {
        console.log("User: " + user.get('name'));

        user.posts().each(function(post) {
            console.log("Comments for post: " + post.get('title'));

            post.comments().each(function(comment) {
                console.log(comment.get('message'));
            });
        });
    }
});

上面我们创建的每一个hasMany关联都会产生一个新函数并加入到这个Model中。我们定义每个User 模型 hasMany Post,这就在User Model中添加一个posts()方法。调用user.posts()会返回一个配置了Post Model的Store。相应的,Post Model会有一个comments()方法,因为我们给它配置了hasMany Comment。

Association不只有助于加载数据,它还有助于创建新纪录:

user.posts().add({
    title: 'Ext JS 4.0 MVC Architecture',
    body: 'It\'s a great Idea to structure your Ext JS Applications using the built in MVC Architecture...'
});

user.posts().sync();

这里我们实例化一个Post,它的user_id字段会被自动府城User的id。调用sync()会通过配置给它的Proxy保存这个新的Post —— 这还是一个异步操作,如果你想当这个操作完成时受到通知,你可以给它传入一个Callback函数。

belongsTo关联也会在Model上生成新方法,看看我们怎么使用:

// get the user reference from the post's belongsTo association
post.getUser(function(user) {
    console.log('Just got the user reference from the post: ' + user.get('name'))
});

// try to change the post's user
post.setUser(100, {
    callback: function(product, operation) {
        if (operation.wasSuccessful()) {
            console.log('Post\'s user was updated');
        } else {
            console.log('Post\'s user could not be updated');
        }
    }
});

这里的加载函数(也就是getUser)也是异步的,你也可一个传入一个Callback函数。setUser方法简单地更新外键(foreign_key,也就这里的user_id)为100,然后保存Post模型,同样,穿进去的Callback函数将会在保存操作完成(无论成功或失败)时被触发。

7. 加载嵌套数据

你可能会质疑,为什么我们给User.load传入一个success函数,但是当我们访问User的post和comment时却不必如此?!那是因为上边的例子我们假设当发起一个获取User的请求时,服务器返回了User数据以及它说关联的Post和Comment数据。通过上边的关联设置,框架会自动解析单个请求中的嵌套数据。为避免为一个User数据发一个请求,然后请求他的所有的Post数据,之后在为每一个Post加载器Comment数据,我们可以直接让服务器返回以上所有的数据:

{
    success: true,
    users: [
        {
            id: 1,
            name: 'Ed',
            age: 25,
            gender: 'male',
            posts: [
                {
                    id   : 12,
                    title: 'All about data in Ext JS 4',
                    body : 'One areas that has seen the most improvement...',
                    comments: [
                        {
                            id: 123,
                            name: 'S Jobs',
                            message: 'One more thing'
                        }
                    ]
                }
            ]
        }
    ]
}

8. 验证

对数据的验证使得Ext JS 4 的模型更加丰富。为了证明,我们基于上边讲解关联的例子做进一步改进。首先向User模型添加一些验证:

Ext.define('User', {
    extend: 'Ext.data.Model',
    fields: ...,

    validations: [
        {type: 'presence', name: 'name'},
        {type: 'length',   name: 'name', min: 5},
        {type: 'format',   name: 'age', matcher: /\d+/},
        {type: 'inclusion', name: 'gender', list: ['male', 'female']},
        {type: 'exclusion', name: 'name', list: ['admin']}
    ],

    proxy: ...
});

验证的格式定义和字段的相同。在上边的每个例子中,我们为验证指定一个字段和类型。上边例子中的验证意思是name字段必须存在,且至少有5个字符,age字段是个数字,gender字段要么是“male”,要么是“female”,username可以是除了“admin”之外的任何值。一些验证还需要其他的额外配置,例如length验证需要min和max属性,format验证需要matcher,等等。Ext JS 4 有5种内建的验证方式,并且添加定制化的验证规则也很简单。下边看一下这些内建的验证方式:

  • presence: 确保字段有值。零算作有效值,但是空字符串不算
  • length: 确保字符串长度在min和max之间。这两个都是可选的。
  • format: 确保一个字符串匹配一个正则表达式。上边的例子意思是确保一个字段是由4个数字后跟至少一个字符组成。
  • inclusion: 确保一个值位于一个指定的集合中(例如,确保性别是male或者female)
  • exclusion: 确保一个值不在一个指定的集合中(例如黑名单用户名,像’admin‘)

现在我们对不能的验证做什么事情有一个了解了,让我们在一个User实例上使用一下。首先创建一个user,然后运行验证方法,注意出现的错误:

// now lets try to create a new user with as many validation errors as we can
var newUser = Ext.create('User', {
    name: 'admin',
    age: 'twenty-nine',
    gender: 'not a valid gender'
});

// run some validation on the new user we just created
var errors = newUser.validate();

console.log('Is User valid?', errors.isValid()); //returns 'false' as there were validation errors
console.log('All Errors:', errors.items); //returns the array of all errors found on this model instance

console.log('Age Errors:', errors.getByField('age')); //returns the errors for the age field

这里的关键函数式validate(),它运行所有配置的验证,然后返回一个Errors对象。Errors对象包含任何发现的错误的集合,再加上一些方便的方法,例如isValid(),如果在任何字段上都没有错误,它会返回true,以及getByField()方法,它会返回一个给定字段上出现的所有错误。

9. 总结

本章主要讲述了Ext JS 4的Data包中的主要功能,从Model、Store、Proxy以及Model的关联以及验证等方面做了详细介绍。通过本文的学习,你能都Ext JS 4的数据操作和交互有一个初步的认识。

10. 参考资料

  1. http://dev.sencha.com/deploy/ext-4.1.0-gpl/docs/index.html#!/guide/data

作者:周邦涛(Timen)
Email:zhoubangtao@gmail.com
转载请注明出处:  http://blog.csdn.net/zhoubangtao/article/details/27707361