ExtJS4 API文档阅读(四)——Data

ExtJS4 API文档阅读(四)——Data

数据

Data包负责加载和保存你应用程序中的所有数据,由41个类构成,其中有三个类是最重要的,分别是模型类(Model),存储类(Store),代理类(Ext.data.proxy.Proxy)。它们几乎在每个应用程序中都被使用到,并且很多相附类为它们提供支持。

ExtJS4 API文档阅读(四)——Data_第1张图片

模型类和存储类

模型类(Ext.data.Model)data包的核心部件。每个模型代表应用程序中的一些数据类型-例如一个电子商务应用程序可以有UsersProductsOrders等模型类。简单来说,模型仅仅是一些域和每个域对应数据的集合。我们将重点研究模型类中的四个主要部分(Fields)、代理(Proxies)、关联(Associations)和验证(Validations)

ExtJS4 API文档阅读(四)——Data_第2张图片

现在让我们看看如何创建一个模型类:

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


模型类通常在存储类中使用,这些存储类主要是一些模型实例的集合。设置存储类和加载它的数据是简单的:

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


我们用Ajax代理( Ajax Proxy)配置我们的存储类,告诉它加载数据的url地址和用来解码数据的读取器(Reader)。这样,我们的服务器将返回JSON格式的数据,我们可以使用设置的Json读取器(Json Reader)解析响应。上面创建的存储类实例从url地址users.json中自动加载一系列User模型类实例的数据。

users.json应该返回如下所示的JSON字符串:

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


请查看Simple Store获取一个演示实例。

内联数据

存储类实例也可以加载内联数据,它们转换每个传递进data中的对象为模型类实例:

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


内联数据的例子(Inline Data example)

排序和分组

存储类实例能在本地执行排序、过滤和分组,同样也提供远程排序、过滤和分组:

Ext.create('Ext.data.Store', {
    model: 'User',
    sorters: ['name', 'id'],
    filters: {
        property: 'name',
        value   : 'Ed'
    },
    groupField: 'age',
    groupDir: 'DESC'
});


我们刚刚创建的存储类实例中,数据首先将按照name排序,其次按id排序;并且数据将被过滤为仅包含nameEdUsers,然后数据将按年龄进行分组且遵循由小到大的顺序。任何时候调用存储类API进行排序、过滤和分组都是是很轻松的。查看排序、分组、过滤存储类实例(Sorting Grouping Filtering Store获取一个演示示例。

代理

代理类被存储类使用以便于管理加载和保存模型类数据。有两种类型的代理:客户端代理(Client)和服务器端代理(Server)客户端代理包括存储数据在浏览器内存的内存方式(Memory)和使用HTML5本地存储器(可用时)的本地存储方式(Local Storage)。服务器端代理操作一些从远程服务器调度来的数据,例如包括AjaxJsonpRest方式。

代理方式可以直接在模型类中定义,如下:

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模型类的存储类实例以相同方式加载数据变得可行,这样我们避免了必须为每个存储类实例复制相同代理方式定义。其次,现在我们可以不使用存储类来加载和保存模型数据:

// Gives us a reference to the User class
// 创建一个User类的引用
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
//我们可以直接保存ed而不用先把它添加到一个存储类中,因为我们配置了
// configured a RestProxy this will automatically send a POST request to the url /users
//一个能自动发送一个POST请求到指定url的Rest代理
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 1并对其一些相关操作(执行一个GET请求到 /users/1)
User.load(1, {
    success: function(user) {
        console.log("Loaded user 1: " + user.get('name'));
    }
});


也有利用HTML5新功能-- LocalStorage SessionStorage  的代理模式。尽管旧的浏览器不支持这些新的HTML5 APIs,它们仍然是有用的,因为很多应用程序将从这些新特性的存在中受益。

直接在模型类中使用代理的例子(Example of a Model that uses a Proxy directly)

关联

模型类之间可以通过关联API链接在一起。大多数应用程序需要处理很多不同的模型类,并且这些模型类之间通常是相关联的。例如一个博客写作应用程序可能有User(用户)Post(文章)Comment(评论)等模型类。每个用户(User)可以创建多篇文章(Posts),并且每篇文章接受很多评论(Comments)。我们可以如下表示这些关系:

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'
});


使得在你的应用程序中表示这种复杂关系变得简单。 每个模型类可以和其他模型类有任意的关联,并且你的模型类可以按任意顺序定义。一旦我们创建了一个模型类实例,我们可以很轻松地遍历与其相关联的数据 -- 例如,如果我们想记录一个给定用户每篇文章的所有相关评论,我们可以如下这样操作

// Loads User with ID 1 and related posts and comments using User's Proxy
//加载User使用ID 1和相关的文章和评论使用User的代理
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关联将产生一个新方法添加到这个模型类上。我们声明的每个User模型类实例有许多(hasMany)文章(Posts),这将为我们添加user.posts()方法,如上面代码段中使用的那样。调用user.posts()方法返回一个配置了Post模型的存储类实例。依次类推,Post模型实例获取了一个comments()方法,因为我们为其设置了hasMany 评论关联。关联不仅对加载数据来说是有用的,而且对创建新记录也是有用的

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赋值给Post中的user_id字段。调用sync()方法,将通过配置的代理方式来保存新创建Post模型类实例  再者,如果你想让操作完成时得到反馈,你可以调用异步操作并传递进一个回调函数来实现。属于(belongsTo)关联也能在模型类实例生成一个新方法,如我们下面介绍的这个示例:

// get the user reference from the post's belongsTo association
//得到user实例引用从post实例的belongsTo关联配置
post.getUser(function(user) {
    console.log('Just got the user reference from the post: ' + user.get('name'))
});
// try to change the post's user
//尝试改变文章的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)是异步调用的,并且需要一个回调函数作为参数来获得user实例。setUser方法仅更新外键(本例中的user_id字段)的值为100,并保存这个Post模型类实例。通常,不管成功与否,当保存操作完成时,传递进去的回调函数都将被触发。

加载内嵌的数据

你或许想知道为什么调用User.load方法时,我们传递一个success方法,但当访问User的文章(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'
                        }
                    ]
                }
            ]
        }
    ]
}


这些数据将被框架自动解析出来。配置模型类的代理方式以用来加载任何地方的数据会变得很轻松,并且它们的阅读器模式几乎可以处理任何格式的响应数据Ext JS 3一样,模型类和存储类在整个框架中被许多组件使用,例如表格,树,表单。

查看关联和验证(Associations and Validations)的演示例以获取一个实际操作并且具有关联关系的模型实例

当然,你可以以一种非嵌套的方式加载你的数据。如果你仅想需要时加载相关的数据,这种懒惰加载模式将可能是有效地。前所做,我们仅加载User数据,除此之外,我们假定返回的响应仅包含User数据,没有任何关联的文章(Post)数据。然后,我们在user.posts().load()方法添加回调函数中以获取相关的文章(Post)数据:

// Loads User with ID 1 User's Proxy
User.load(1, {
    success: function(user) {
        console.log("User: " + user.get('name'));
        // Loads posts for user 1 using Post's Proxy
        user.posts().load({
            callback: function(posts, operation) {
                Ext.each(posts, function(post) {
                    console.log("Comments for post: " + post.get('title'));
                    post.comments().each(function(comment) {
                        console.log(comment.get('message'));
                    });
                });
            }
        });
    }
});


查看懒惰关联(Lazy Associations)模式可以获取一个完整的示例

验证

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域的值只能为malefemale,并且用户名可以为除了admin外的其他任何名称。一些验证器可能具有其他的可选配置 -- 例如,长度验证可以具有最大和最小属性,格式(format)具有匹配(matcher)属性等。Ext JS有五种内置的验证器,且可以轻松地添加用户自定义规则。首先让我们看看这五种类型:

  • 存在(presence)  确保该域必须有确定值。0被看作有效值,但空字符串将被视为有效值。
  • 长度(length)  确保一个字符串长度位于最大和最小值之间。两个参数都是可选的。
  • 格式(format)  确保一个字符串匹配指定的正则表达式。上例中我们确保age域必须为数字。
  • 包含(inclusion) 确保该域的值在指定的数值集合中(例如确保性别只能是男或女)。
  • 排除(exclusion) 确保该域的值不在指定的数值集合中(例如用户名不能为admin

既然我们已经理解了不同验证器的功能,让我们尝试在一个User实例中使用它们。

我们创建一个user实例并在其中运行验证器,注意产生的任何错误:

// now lets try to create a new user with as many validation errors as we can
// 现在让我们尝试创建一个user实例,并产生尽量多的验证错误
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
// 在我们刚刚创建的user实例中运行一些验证器
var errors = newUser.validate();
console.log('Is User valid?', errors.isValid()); //returns 'false' as there were validation errors 当有验证器错误产生时,返回false
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返回age域产生的错误

这里的关键函数是validate(),该函数运行所有配置验证器并返回一个Errors对象。这个简单的对象为所有错误的集合,并且添加了一些便利的方法,例如isValid() -- 当任何域都没有错误产生时,返回true,还有getByField()法,返回给定域产生的所有错误。

请查看关联和验证(Associations and Validations)示例以获取一个使用验证器的复杂例子。

你可能感兴趣的:(ExtJs,Data)