Sencha Touch 2.0官方指南:使用data包
大多數(shù)情況下,data包中的model,store和proxy的用法如下:
- Model: model代表你應用關(guān)心的一個實體。用戶,聯(lián)系人,地址,和產(chǎn)品都可以是model。簡單地說,model只是字段(field)和相應數(shù)據(jù)的集合,當然,它可以做得更多。
- Store: store只是一個model的實例集合。大多數(shù)情況下,它只是一個高級的數(shù)組,當然它也提供如排序,篩選和分組,以及引發(fā)事件等功能。
- Proxy: proxy負責實現(xiàn)所有加載和保存數(shù)據(jù)的工作。通常你會創(chuàng)建一個AJAX proxy從你的服務器獲取數(shù)據(jù)并加載到store。
model 和 store
最簡單的Model僅僅是一些字段(field)和相應數(shù)據(jù)的集合。我們來看看Ext.data.Model的四個主要部分 — Fields,Proxies, Associations 和 Validations.
現(xiàn)在讓我們來看看怎樣創(chuàng)建一個model:
- Ext.define('User', {
- extend: 'Ext.data.Model',
- fields: [
- { name: 'id', type: 'int' },
- { name: 'name', type: 'string' }
- ]
- });
model通常和一個store一起使用,store基本上是一個model實例集合。創(chuàng)建一個store并加載數(shù)據(jù)很簡單:
- Ext.create('Ext.data.Store', {
- model: 'User',
- proxy: {
- type: 'ajax',
- url : 'users.json',
- reader: 'json'
- },
- autoLoad: true
- });
我們在store里配置了Ajax Proxy,提供URL名稱,要求返回的數(shù)據(jù)編碼格式能夠被Reader正常使用。在本例中我們的服務器返回JSON格式的數(shù)據(jù),所以我們已經(jīng)建立了一個JSON Reader來讀取返回數(shù)據(jù)。store從URL users.json自動加載了model User的實例集合。users.json的URL應該返回一個JSON字符串,像這樣:
- {
- success: true,
- users: [
- { id: 1, name: 'Ed' },
- { id: 2, name: 'Tommy' }
- ]
- }
實時效果,請見Simple Store示例。
內(nèi)部數(shù)據(jù)
Store也可以加載內(nèi)部數(shù)據(jù)。在內(nèi)部,Store會把我們傳遞的所有數(shù)據(jù)對象轉(zhuǎn)為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' }
- ]
- });
Inline Data 示例
排序和分組
Store既能夠進行本地排序,篩選和分組,也支持遠程排序,篩選,和分組:
- Ext.create('Ext.data.Store', {
- model: 'User',
- sorters: ['name', 'id'],
- filters: {
- property: 'name',
- value : 'Ed'
- },
- groupField: 'age',
- groupDir: 'DESC'
- });
在我們剛剛創(chuàng)建的Store中,數(shù)據(jù)將首先由name排序,然后由id排序;數(shù)據(jù)只包含過濾條件name等于Ed的用戶,按age降序分組。隨時都可以簡單地通過Store API改變排序,篩選和分組。實時效果,請見Sorting Grouping Filtering Store 示例。
Proxy
proxy被store用于處理model數(shù)據(jù)加載和保存。proxy有兩種類型:客戶端和服務器端。在客戶端Proxy例子里,如果HTML5的本地存儲特性可用,則可以使用瀏覽器內(nèi)存和本地存儲來存儲數(shù)據(jù)。服務器端proxy處理遠程服務器的封裝數(shù)據(jù),例如AJAX,JSONP和Rest。
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都需要以同樣的方法加載數(shù)據(jù),因此我們需要避免重復為每個store定義proxy。其次,我們可以不使用store直接加載和保存model數(shù)據(jù):
- // 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'));
- }
- });
porxy也可以利用HTML5新功能的優(yōu)勢 - LocalStorage和SessionStorage。雖然老的瀏覽器不支持HTML5的這些新API,但是對很多應用程序來說,使用它們的將可以使你受益匪淺。Model直接使用Proxy的示例。
關(guān)聯(lián)關(guān)系
Model之間可以通過Associations API建立關(guān)聯(lián)關(guān)系。大多數(shù)應用程序需要處理許多不同的Model,Model之間總是有各種關(guān)聯(lián)。一個博客應用程序可能有User,Post和Comment這些model。每個用戶會創(chuàng)建許多帖子,每篇帖子又會收到許多評論。我們可以像這樣表達這些關(guān)系:
- 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之間豐富關(guān)系表示出來。每個model可以與多個其他model建立關(guān)聯(lián),你的model也可以在任意命令中定義。一旦我們有一個model的實例,我們可以很容易地遍歷相關(guān)的數(shù)據(jù)。例如,記錄一個選定用戶在所有博文上的留言,可以這樣做:
- // 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'));
- });
- });
- }
- });
我們上面創(chuàng)建的model都添加了一個新的方法hasMany。這是聲明每個User model有多個Post。在上面的代碼片段中,我們用到了user.posts()方法。調(diào)用user.posts()方法返回一個Post model配置的store。同樣,因為在Post model也聲明了與Comment的hasMany關(guān)系,我們可以調(diào)用comments()方法。
關(guān)聯(lián)關(guān)系不僅在加載數(shù)據(jù)時有用,在添加新記錄時也有用:
- 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字段添加這個用戶的id。調(diào)用sync()方法可以通過配置的proxy保存這個新Post對象。你別看這個方法名稱叫sync(),這是一個異步操作,如果你想在操作完成時得到通知,你要傳遞一個回調(diào)方法。
在model中,也有新的方法生成belongsTo關(guān)系。像下面的代碼這樣使用:
- // 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');
- }
- }
- });
再次強調(diào),getUser這種讀取數(shù)據(jù)的方法是異步的,必須有一個回調(diào)方法來獲取用戶實例。setUser方法只需要更新外鍵(在這個例子里面是user_id)為100,再保存Post model。同樣的,只要保存操作結(jié)束,不管是否保存成功,回調(diào)方法都會被執(zhí)行。
加載嵌套數(shù)據(jù)
你也許會奇怪,為什么我們在調(diào)用User.load時會傳遞一個success方法,但是在存取用戶的帖子和評論時又沒有這樣做。這是因為上面的例子中我們假定當我請求獲取一個用戶時,服務器返回的用戶數(shù)據(jù)已經(jīng)嵌套了該用戶的所有帖子和評論。通過建立前面例子中那樣的關(guān)聯(lián)關(guān)系,只需要一次請求框架可以自動解析出嵌套的數(shù)據(jù)。服務器一次返回所有數(shù)據(jù),不僅是用戶數(shù)據(jù),還包括帖子數(shù)據(jù)和評論數(shù)據(jù),數(shù)據(jù)格式如下:
- {
- success: true,
- users: [
- {
- id: 1,
- name: 'Ed',
- age: 25,
- gender: 'male',
- posts: [
- {
- id : 12,
- title: 'All about data in Sencha Touch 2',
- body : 'One areas that has seen the most improvement...',
- comments: [
- {
- id: 123,
- name: 'S Jobs',
- message: 'One more thing'
- }
- ]
- }
- ]
- }
- ]
- }
這些數(shù)據(jù)全部被框架自動解析。你可以很容易從幾乎所有地方通過配置model的proxy加載數(shù)據(jù),然后通過它們的reader處理返回格式。像Sencha Touch1一樣,model和store在整個框架中被很多個組件用到,如Grid,Tree,F(xiàn)orm等等。
在model中怎么樣使用這些關(guān)聯(lián)關(guān)系,請看 Associations and Validations 示例。
當然,我們很可能使用非嵌套的方式加載數(shù)據(jù)。如果你需要“懶加載”(lazy load)相關(guān)數(shù)據(jù),這可能對你有用。假定返回的數(shù)據(jù)只有用戶數(shù)據(jù)而不包含任務關(guān)聯(lián)的帖子,像我們以前做的,我們只需要加載用戶數(shù)據(jù)。然后,我們要在回調(diào)方法里面調(diào)用user.posts().load()方法以獲取相關(guān)的帖子數(shù)據(jù):
- // 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
驗證
Sencha Touch2的Model對數(shù)據(jù)驗證有豐富的支持。為了證明這一點,我們將創(chuàng)建一些例子來說明。首先,讓我們在User model里添加一些驗證:
- 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字段必須為數(shù)字,gender字段的值必須是“male”或者“female”,username可以是admin之外的任意值。有些驗證采取額外的可選配置 - 例如長度驗證可以使用Min和Max屬性,格式可以使用匹配正則表達式等等。Sencha Touch 2內(nèi)置了5種驗證,添加自定義規(guī)則也很容易。首先,讓我們看看內(nèi)置的:
- presence 確保該字段有一個值。0是一個有效的值,但空字符串不是的。
- length 確保一個字符串的長度在最小和最大長度之間。這兩個約束是可選的。
- format 確保一個字符串匹配一個正則表達式格式。在上面的例子中,我們必須確保年齡字段是由至少一個以上的數(shù)字。
- inclusion 確保一個值是在一個特定值的集合內(nèi)(例如,確保性別是男或女)。
- exclusion 確保一個值不在一個特定值的集合內(nèi)(例如,列入黑名單的用戶名,如“管理員”)。
現(xiàn)在,我們已經(jīng)掌握了不同的驗證,讓我們嘗試對用戶實例中使用它們。我們將創(chuàng)建一個用戶,針對它運行驗證,并提示失敗信息:
- // 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
這里關(guān)鍵的方法是validate(),它運行所有配置的驗證,并返回一個錯誤對象。這個簡單的對象僅僅是一個已找到的所有錯誤集合,加上一些方便的方法如isValid(),如果所有字段都沒有錯誤則返回true,getByField()方法返回一個給定字段中的所有錯誤。
使用驗證的完整示例請見 Associations and Validations
延伸閱讀
數(shù)據(jù)只是整個Sencha Touch 2 生態(tài)系統(tǒng)的一部分。要了解更多框架內(nèi)容以及它是怎樣工作的,我們建議繼續(xù)閱讀如下文章:
原文地址:http://html5mobi.gotoip1.com/discussion/222/sencha-touch-2-sencha-touch2-data-the-data-package
【編輯推薦】