ExtJS 4 數據(包)詳解 - ExtJS4中文教程

這裏的包和java的包同義,不是指“數據包”,這個數據包是指Ext.data
data包是負責加載和保存應用中的數據的包,有41個類,其中三個是最重要的:Model,Store,Ext.data.proxy.Proxy,幾乎每個應用都要用到它們,有若干個支持類輔助它們:

data-package

Models and Stores 模型和存儲器

data包的中心是Ext.data.Model,模型代表了應用中的一些數據類型,例如電子商務應用中可能會有Users,Products,Orders等模型,最簡單的模型就是一組字段和它們的數據,看一下模型的四個主要部分,Fields,Proxies,Associations,Validations

model

讓我們看一下,如何創建一個模型:

1
2
3
4
5
6
7
Ext.define('User', {
    extend: 'Ext.data.Model',
    fields: [
        { name: 'id', type: 'int' },
        { name: 'name', type: 'string' }
    ]
});

Model的一個典型用處就是用在Store中,Store就是Model實例的集合,構建一個Store並加載數據是很簡單的:

1
2
3
4
5
6
7
8
9
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中自動加載一組User模型的實例,users.json需要輸出的內容應該類似這樣:

1
2
3
4
5
6
7
{
    success: true,
    users: [
        { id: 1, name: 'Ed' },
        { id: 2, name: 'Tommy' }
    ]
}

Inline data 內聯數據

Store還可以讀取內聯的數據,這種情況Store內部會轉換我們內聯的每個對象作爲Model實例的數據:

1
2
3
4
5
6
7
8
9
Ext.create('Ext.data.Store', {
    model: 'User',
    data: [
        { firstName: 'Ed',    lastName: 'Spencer' },
        { firstName: 'Tommy', lastName: 'Maintz' },
        { firstName: 'Aaron', lastName: 'Conran' },
        { firstName: 'Jamie', lastName: 'Avins' }
    ]
});

Sorting and Grouping 排序和分組

Store同時支持本地和遠程的排序、過濾、分組:

1
2
3
4
5
6
7
8
9
10
11
Ext.create('Ext.data.Store', {
    model: 'User',

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

這段代碼創建的store,數據會先根據name後根據id來排序,name不是Ed的Users會被過濾掉,而且數據還會根據年齡分組並且倒序。通過Store的api改變排序、過濾、分組都很容易

Proxies 代理

代理是store用來加載和保存模型數據的工具,有兩種代理,客戶端和服務器端。客戶端代理例如Memory內存代理是完全在瀏覽器內存中的,還有HTML5的本地存儲代理。服務器端代理處理和遠程服務器間的數據通信例如AJAX,JSONP,REST等方式。
代理可以直接由模型定義:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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'
});

這麼做有兩點好處。第一是假如不同的store都使用User模型,而且假如在每個store中User模型加載數據的方式都相同,這時候在User模型中指定代理,就不需要在多個store中重複指定代理。第二是我們可以讓模型直接加載和保存數據而不需要store。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 獲取 User 模型的引用
var User = Ext.ModelMgr.getModel('User');

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

// 我們可以直接保存Ed的數據而不用通過Store
// 因爲我們在User模型中配置了一個RestProxy代理,這可以自動發起POST請求到url /users
ed.save({
    success: function(ed) {
        console.log("Saved Ed! His ID is "+ ed.getId());
    }
});

// 加載User 1 然後處理 (通過 GET 請求到 url /users/1)
User.load(1, {
    success: function(user) {
        console.log("Loaded user 1: " + user.get('name'));
    }
});

有些代理可以受益於HTML5的本地存儲LocalStorageSessionStorage,儘管老舊瀏覽器不支持這些特性,但是很多應用還是可以受益

Associations 關聯

模型之間可以通過關聯API關聯到一起,大多數應用都處理許多不同的模型,並且模型總是相關的。一個博客應用可能有User,Post,Comment模型,每個User創建Post,每個Post接收評論Comment,我們可以這樣表達它們之間的關係:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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'
});

模型之間表示關聯非常容易,每個模型可以有隨意多的和其他模型的關聯,而且定義模型的先後順序不受限制。當我們有一個模型的實例,我們就可以很容易的遍歷出和它相關的數據,例如我們想打印出一個User所有文章的所有評論:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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創建一個新function,例如我們聲明瞭User hasMany Posts,這就會給User模型創建一個user.posts()方法,調用這個方法會返回一個裝有Post模型的StorePost也同樣獲得了一個comments方法
關聯不僅幫助我們加載數據,創建新record時也很方便:

1
2
3
4
5
6
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實例,sync是個移步調用,如果想在sync完成之後獲得通知,可以傳一個callback函數。
belongsTo關聯也會爲模型創建一個新方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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也是異步調用,需要傳遞迴調函數來獲取user實例。setUser方法更新了外鍵(user_id),並且保存Post模型,也可以傳遞迴調函數用來得知操作是否成功。

Loading Nested Data 加載嵌套數據

你可能會奇怪爲什麼只傳了一個success函數給User.load,但是獲取User的posts和comments時卻不用傳遞迴調函數。這是因爲上面的例子我們假設了服務器端會在user數據一起返回它相關的post和comment數據。設置了關聯關係,框架會自動從user的請求數據中解析post和comment的數據,而不是需要post數據時發新請求,需要comment的數據時又發新請求(作者注:如果每個post和comment都單獨請求數據,會給服務器造成很大壓力),我們可以像這樣返回數據:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
    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'
                        }
                    ]
                }
            ]
        }
    ]
}

框架會自動解析數據。很容易配置模型的代理從任意數據源獲取數據,各種數據格式基本都有相應的Reader可以hold住。ExtJS 3中Store被各種組件使用,例如Grid, Tree, Form
當然也可以用非嵌套的方式加載數據,在’lazy load’的時候這種做法是有用的,只加載需要的數據。讓我們像之前一樣加載User的數據,這次假設服務器端只返回了User的數據,不包括相關聯的其他模型的數據,然後調用user.posts().load() 並傳遞迴調函數接收加載的Post數據:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 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'));
                    });
                });
            }
        });
    }
});

Validations 校驗

ExtJS4的模型Model功能開始變的豐富起來,並且支持數據校驗,我們繼續上面的例子來展現如何使用數據校驗,首先添加對User的數據校驗:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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匹配,ExtJS內置了5種校驗器,先來看一下內置的5種校驗器:

  • presence 確保這個字段被設置了一個值,空數組是個有效的值,但是空字符串不是
  • length 確保這個字段的長度在最大值最小值之間,最大值和最小值都是可選參數
  • format 確保這個字段符合一個正則表達式
  • inclusion 確保字段在給定的值的集合內,在枚舉集合種
  • exclusion 確保字段不在給定的集合種

現在我們已經知道了不同的校驗器做了什麼事情,通過User使用一下,創建一個User然後執行校驗:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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對象,這個對象含有所有錯誤信息,還有一些常用方法例如isValid,如果沒有任何錯誤isValid的返回值是true,getByField方法可以用來獲取某一個診斷錯誤。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章