原文位址:http://www.cnblogs.com/dowinning/archive/2012/02/29/2373817.html
前言:
資料模型[Model]的主要職責是描述存儲和管理應用程式的資料,堪稱MVC應用程式的肌肉群組織,缺少了Model的應用程式隻能是一具沒多大實用價值的空殼。事實上,幾乎目前所有的網際網路應用程式都是以資料的傳遞和互動為主要目的。
這篇文章的英文原址是http://docs.sencha.com/touch/2-0/#!/guide/models
原文标題是:Using Models(使用資料模型)。在官方文檔目錄中,它事實上的地位是MVC概述之後開篇三闆斧之一,鑒于Sencha Touch MVC的特點,這三闆斧的介紹順序是倒過來的,先C控制器再V視圖最後才是M資料模型,但是其重要性卻不分先後。
Sencha Touch 交流QQ群213119459歡迎您的加入。
Using Models in your Applications
在應用程式中使用資料模型
注:為友善起見,文中所有出現 Sencha Touch 的地方均以 ST 簡寫替代。
At its simplest a Model is just a set of fields and their data. We’re going to look at four of the principal parts of Ext.data.Model — Fields, Proxies, Associations and Validations.
在最簡單的情況下,一個資料模型隻是一組字段和資料,我們來看一下Ext.data.Model的四個主要部分,Fields(字段), Proxies(代理), Associations(關聯),Validations(驗證)。
Let's look at how we create a model now:
來看一下如何建立一個資料模型:
Ext.define('User', {
extend: 'Ext.data.Model',
fields: [
{ name: 'id', type: 'int' },
{ name: 'name', type: 'string' }
]
});
Using Proxies
使用代理
Proxies are used by stores to handle the loading and saving of model data. There are two types of proxy: client and server. Examples of client proxies include Memory for storing data in the browser's memory and Local Storage which uses the HTML 5 local storage feature when available. Server proxies handle the marshaling of data to some remote server and examples include Ajax, JsonP, and Rest.
通常store會使用proxy來處理model資料的加載和存儲。Proxy有兩種類型,用戶端和服務端。用戶端類型的例子裡包含了浏覽器記憶體存儲和html5本地存儲兩種。服務端代理通過配置遠端伺服器來擷取資料,例子包含了Ajax/JsonP/Rest的方式。
Proxies can be defined directly on a model, like so:
代理可以直接配置在資料模型上,比如這樣:
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(使用User資料模型代理)
Ext.create('Ext.data.Store', {
model: 'User'
});
This helps in two ways. First, it's likely that every store that uses the User model will need to load its data the same way, so we avoid having to duplicate the proxy definition for each store. Second, we can now load and save model data without a store:
這麼做有兩個好處,首先每一個使用User資料模型的store都通過同樣的方式加載資料,這樣我們就無需把proxy的配置每個地方複制一遍了。第二我們可以在不需要store的情況下就加載和儲存資料。
// 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 configured a RestProxy this will automatically send a POST request
// to the url /users
// 我們可以直接儲存Ed而無需把它加入到Store,因為我們已經配置了一個RestProxy
// 它會自動發送一個Post請求到/users這個url
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'));
}
});
There are also proxies that take advantage of the new capabilities of HTML5 - LocalStorage and SessionStorage. Although older browsers don't support these new HTML5 APIs, they're so useful that a lot of applications will benefit enormously by using them.
另外一種proxy類型利用了html5的新能力——本地存儲和會話存儲。雖然老一些的浏覽器不支援這些html5的API,但它們依然很有用,很多應用程式都可以通過使用它們獲益。
Example of a Model that uses a Proxy directly(該連結是一個使用了代理的Model例子)
Associations
關聯
Models can be linked together with the Associations API. Most applications deal with many different models, and the models are almost always related. A blog authoring application might have models for User, Post, and Comment. Each user creates posts and each post receives comments. We can express those relationships like so:
資料模型可以通過Association API關聯在一起。大部分應用程式都要處理很多不同的資料模型,資料模型之間幾乎總是存在關聯。一個部落格應用程式可能會包含使用者、發帖和評論等資料模型。每一個使用者建立很多文章,每個文章又會收到很多評論。我們可以像下面這樣來定義他們的關聯。
Ext.define('User', {
extend: 'Ext.data.Model',
fields: ['id', 'name'],
proxy: {
type: 'rest',
url : 'data/users',
reader: {
type: 'json',
root: 'users'
}
},
hasMany: 'Post' // 等價于 { 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'
});
It's easy to express rich relationships between different models in your application. Each model can have any number of associations with other models and your models can be defined in any order. Once we have a model instance we can easily traverse the associated data. For example, to log all comments made on each post for a given user, do something like this:
在應用程式中描述不同資料模型之間的豐富關聯是很容易的。每一個資料模型都可以包含無限多個關聯關系,你的資料模型定義順序也可以随意。我們得到一個資料模型的執行個體之後就可以輕松地把它跟它的相關資料貫穿起來。比如要列出給定使用者每個文章中的全部評論,我們可以這樣做:
// Loads User with ID 1 and related posts and comments using User's Proxy
// 使用User的代理來加載ID為1的使用者和他的相關文章及評論
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'));
});
});
}
});
Each of the hasMany associations we created above adds a new function to the Model. We declared that each User model hasMany Posts, which added the user.posts() function we used in the snippet above. Calling user.posts() returns a Store configured with the Post model. In turn, the Post model gets a comments() function because of the hasMany Comments association we set up.
上面例子中的每個hasMany關聯都會自動建立一個新的方法。比如我們定義了“每個User資料模型都hasMany個post”,是以一個user.posts()的方法被自動建立出來。調用這個方法就會傳回一個由post資料模型定義的Store,同理,由于我們定義了post資料模型hasMany個comment模型,是以也會得到一個post.comments()方法。
Associations aren't just helpful for loading data, they're useful for creating new records too:
關聯不僅僅對加載資料有幫助,在建立新資料時依然很有用:
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();
Here we instantiate a new Post, which is automatically given the User id in the user_id field. Calling sync() saves the new Post via its configured proxy. This, again, is an asynchronous operation to which you can pass a callback if you want to be notified when the operation completed.
這裡我們執行個體化一個新的post,它的user_id字段會自動被賦予給定user的id。調用sync()方法可以通過配置的代理來儲存新post。這同樣是一個異步的操作,如果你想得到操作結束的提示可以傳一個callback函數進去。
The belongsTo association also generates new methods on the model. Here's how to use that:
belongsTo關聯同樣會産生資料模型的新方法,示例如下:
// get the user reference from the post's belongsTo association
// 通過post的belongsTo關聯獲得對user的引用
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的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');
}
}
});
Once more, the loading function (getUser) is asynchronous and requires a callback function to get at the user instance. The setUser method simply updates the foreign_key (user_id in this case) to 100 and saves the Post model. As usual, callbacks can be passed in that will be triggered when the save operation has completed, whether successfully or not.
getUser是一個異步方法,是以需要一個回調函數來獲得傳回的user執行個體。而setUser方法把外鍵(user_id)更改成為100并應用對post資料的更改。像往常一樣,我們傳入一個回調函數以確定儲存操作結束的時候會被觸發,不論是否成功(譯者注:操作結束意味着網絡層面資料互動的完成,而是否成功則代表邏輯層面更新資料的成敗,千萬不要混淆)。
Validations
驗證
Sencha Touch 2 Models have rich support for validating their data. To demonstrate this we're going to build upon the example we created that illustrated associations. First, let's add some validations to the User model:
ST2的資料模型對資料驗證有着豐富的支援。為了示範這些,我們繼續在前面建立的例子基礎之上進行擴充。首先給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: ...
});
Validations follow the same format as field definitions. In each case, we specify a field and a type of validation. The validations in our example are expecting the name field to be present and to be at least five characters in length, the age field to be a number, the gender field to be either "male" or "female", and the username to be anything but "admin". Some validations take additional optional configuration - for example the length validation can take min and max properties, format can take a matcher, etc. There are five validations built into Sencha Touch 2, and adding custom rules is easy. First, let's look at the ones built right in:
驗證功能遵循與field定義同樣的格式。每一次我們隻能定義一個字段的一種驗證類型。例子當中的驗證要求name字段是必須的并且最少5個字元,age字段必須是數字,gender字段要麼是male要麼是female,username不能為admin。某些驗證還可能使用更多選項配置,比如字元長度可以有min和max屬性,format可以是一個matcher等等。ST2内置有5種驗證類型,想要增加自定義規則同樣很簡單,首先看一下内置的:
presence simply ensures that the field has a value. Zero counts as a valid value but empty strings do not.
Presence保證某個字段必須有一個值。如:0是一個有效地值,但空字元串則不行。
length ensures that a string is between a minimum and maximum length. Both constraints are optional.
Length確定字元串的長度在許可範圍之内,不過這兩個限制條件是可選的。
format ensures that a string matches a regular expression format. In the example above we ensure that the age field is four numbers followed by at least one letter.
Format確定字元串符合正規表達式的規定。例子當中我們要求age字段是至少4個數字并且後面跟着1個字母(莫非文檔作者暈了頭?沒看出代碼中是這麼限制的呀)
inclusion ensures that a value is within a specific set of values (for example, ensuring gender is either male or female).
Inclusion保證其值必須是指定範圍其中之一(比如性别要麼是男要麼是女)
exclusion ensures that a value is not one of the specific set of values (for example, blacklisting usernames like 'admin').
Exclusion保證值不能是指定範圍的其中之一(比如username的黑名單就禁止admin)
Now that we have a grasp of what the different validations do, let's try using them against a User instance. We'll create a user and run the validations against it, noting any failures:
現在我們掌握了不同驗證規則類型的定義,下面來嘗試一下如何應用到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(有錯誤無法通過驗證,是以當然會傳回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字段的所有錯誤)
The key function here is validate(), which runs all of the configured validations and returns an Errors object. This simple object is just a collection of any errors that were found, plus some convenience methods such as isValid(), which returns true if there were no errors on any field, and getByField(), which returns all errors for a given field.
這兒的核心代碼是validate()方法,該方法會運作validation中配置的所有規則檢查并傳回錯誤對象。這個對象是一個由所有錯誤組成的數組組合,同時提供了一系列便捷的方法,比如isValid()在所有字段都沒出現錯誤的時候就會傳回true,而getByField()方法會傳回指定字段的所有錯誤。
For a complete example that uses validations please seeAssociations and Validations。
完整的驗證執行個體請參見這裡Associations and Validations