用了Extjs快一年了,這裏整理一下model。html
Extjs 中數據包總共包含了40多個類,其中有三個類比其餘類有更重要的地位,它們分別是:model
、store
和proxy
,這些類在大部分的應用系統中都會用到而且獲得了大量衛星類的支持,如上圖。數據包的中心是Ext.data.Model
,一個model表明系統中的一些數據的類型,好比:咱們的成果管理系統中的論文、專利、獎勵的model。它是真實世界中實體對象在應用系統中的反映。前端
咱們來看下在Ext中model代碼:web
Ext.define('Srims.model.patent.patents', { extend: 'Ext.data.Model', fields: [ { name: 'id', type: 'int', mapping: 'Id'}, { name: 'Title', type: 'string', mapping: 'Title'}, { name: 'PatentNumber', type: 'string', mapping: 'PatentNumber'}, { name: 'PatentType', type: 'string', mapping: 'PatentType'}, { name: 'PatentStatus', type: 'string'}, { name: 'AuthorizeDateTime',type: 'string', mapping: 'AuthorizeDateTime'}, { name: 'DepartmentId', type: 'int'}, { name: 'DepartmentName', type: 'string', mapping: 'DepartmentName'}, { name: 'ResponsorName', type: 'string', mapping: 'ResponsorName'}, { name: 'Status', type: 'string'} ]
});ajax
這是一個最簡單的model,只有fields,接下來咱們還會講到model的四個主要的部分--Fields,Proxy,Association 和 Validations。正則表達式
一般,model和store一塊兒使用,若是說model是數據的類型,它的實例是一條數據記錄,那麼store實例是model實例的集合,建立一個store而且加載數據。json
Ext.define("Srims.store.patent.patents", { extend: 'Ext.data.Store', model: 'Srims.model.patent.patents', proxy: { type: 'ajax', url:'http://222.195.150.214:24367/api/patents/getbystatus/1', reader: { type: 'json', root: 'DataList', successProperty: 'success' } }, autoLoad: true });
咱們給store配置了一個Ajax Proxy(後面講),告訴他URL,讓它從這個地方獲取數據,還配了reader去解析數據(reader後面也講),服務器返回Json,因此咱們建立了一個Json reader 去讀取響應(response)。Store自動加載一組model 實例,就像這樣api
Store還提供了對數據的過濾、排序、分組的功能:跨域
Ext.define("Srims.store.patent.patents", { extend: 'Ext.data.Store', model: 'Srims.model.patent.patents', //…… sorters: ['Title', 'id']; filters: { property: 'DepartmentName', value: '信息科學與工程學院' } groupField: 'status', groupDir: 'Desc' });
在這個store中,咱們將數據先按名稱排序,再按Id排序;而後過濾爲只有學院爲信息科學與工程學院的記錄,另外,根據記錄狀態按照降序分組。數組
由於store API中有不少有關的方法,能夠根據須要隨時更改store的排序、過濾、分組方式。好比,在成果管理系統中,所屬項目列表,我要將列表中狀態爲添加(A)和刪除(D)的記錄拿出來,就可使用filter。瀏覽器
//給store添加過濾器 store.filter([ function(item) { return (item.data.Status == 'A') || (item.data.Status == 'D'); } ]); …… //清除過濾器 store().clearFilter();
必定要注意,必定要在結束後加上這句store().clearFilter(); 將該過濾器清除,否則以後store中就只有這些過濾後的數據。
上圖清晰地展現了model的4個重要組成部分。(字段定義、數據代理、模型關聯、數據校驗)。
Proxy被用來對model數據的讀取和保存的,有兩種proxy:客戶端proxy(Ext.data.proxy.Client
)和服務器端proxy(Ext.data.proxy.Server
),它們都繼承自Ext.data.proxy.Proxy
類。
Ext.data.proxy.Client
是在客戶端進行數據存取操做的基類,一般不會進行實例化,而是使用它的子類。Ext.data.proxy.Memory
使用內存變量存取數據:Ext.data.proxy.LocalStorage
和Ext.data.proxy.SessionStorage
是Ext.data.proxy.WebStorage
使用HTML5的新特性DOM Storage機制,用於存儲鍵值對。SessionStorage用於存儲與當前瀏覽器窗口關聯的數據,窗口關閉後,sessionStorage中存儲的數據將沒法使用;localStorage用於長期存儲數據,窗口關閉後,localStorage中的數據仍然能夠被訪問,全部瀏覽器窗口能夠共享localStorage的數據。
Ext.data.proxy.Server
是服務器端代理的父類,通常也不進行實例化。與客戶端代理不一樣,服務器代理會訪問遠程的服務器資源,適合於長期保存重要的數據資料。
Ext.data.proxy.Ajax
代理是一個在應用程序中使用最普遍的服務器端代理(也是成果管理系統中主要使用的代理),它採用Ajax方式經過請求指定的URL來讀寫數據,他不能讀取跨域數據(Ext.data.proxy.JsonP
主要用於跨域讀取數據)。
成果系統中當時由於index.html文件在本地,要請求214服務器上的數據,產生了跨域問題,後來把index文件頁放在214上,請求同在214上的數據,避免了跨域問題。
Ajax代理髮起請求是會自動插入sorting排序、filtering過濾、paging分頁和grouping分組設置到每個請求中,在成果系統中,在store中這樣配置,完成分頁:
Ext.define("Srims.store.achievementAudit.auditPatentList", { extend: 'Ext.data.Store', model: 'Srims.model.patent.patents', pageSize : 40, //分頁-每頁記錄數 proxy: { type: 'ajax', url: Srims.api.patentlist + '2', reader: { totalProperty: ‘TotalRecordCount’, //獲取接收到的數據的記錄總數 type: 'json', root: 'DataList', successProperty: 'success' } }, autoLoad: true });
數據讀取器主要用於將數據代理讀取到的原始數據按照不一樣的規則進行解析,將解析後的數據保存在Model模型對象中。數據讀取器至關於原始數據格式與Extjs標準數據格式之間的橋樑,它屏蔽了原始數據格式不一樣對程序開發形成的影響。在Extjs中提供的數據解析器主要有以下3種:
Ext.data.reader.Json (JSON數據讀取器)
Ext.data.reader.Xml (XML數據讀取器)
Ext.data.reader.Array (數組數據讀取器)
reader: { type: 'json', root: 'DataList', //返回信息的屬性名 totalProperty: 'TotalRecordCount', //獲取記錄總數的屬性名 }
數據寫入器主要用於將數據代理提交到服務器的數據進行編碼,至關於Extjs標準數據格式與服務器數據格式之間的橋樑,他屏蔽了服務器端數據格式不一樣對程序開發形成的影響。在Extjs中提供的數據寫入器有:
Ext.data.writer.Json (Json寫入器)
Ext.data.writer.Xml (xml寫入器)
--
Ext.define("User", { extend:"Ext.data.Model", fields:[ {name:'name', type:'string'}, {name:'age', type:'int'} ], proxy:{ type : "ajax", url : " fakeData.jsp", writer:{ type : "json" } } }); var user = Ext.ModelMgr.create({ name: "Tom", age: 24 } ,"User"); user.save();
Ext.data.proxy.Rest是一個特殊化的Ajax代理,將四種動做(create,read,update和destroy)映射到四種restful http動詞(put,get,post和delete)上,將請求的URL轉化爲rest風格,方便進行rest風格的web應用開發。
rest代理會根據前端框架狀況,判斷要執行的操做,從而判斷用什麼方法及須要訪問的資源,最終肯定URL。好比,在調用save方法時,會自動判斷Model的id屬性是否有值若是有就使用update路徑,若是沒有就使用create路徑:
Ext.define('User', { extend: 'Ext.data.Model', fields: ['id', 'name', 'email'], proxy: { type: 'rest', url : '/users' } }); var user = Ext.create('User', { name: 'Ed Spencer', email: 'ed@sencha.com' }); user.save(); //POST /users //擴展save,添加回調函數 user.save({ success: function(user) { user.set('name', 'Khan Noonien Singh'); user.save(); //PUT /users/123 } }); user.destroy(); //DELETE /users/123
在應用系統中總會有不一樣的模型,這些模型之間大部分狀況下是有關聯的,好比,成果系統中,論文和做者、專利和發明人、獎勵和獲獎人之間存在一對多的關係,在Extjs4中支持的關聯關係包括一對多和多對一兩種,分別經過Ext.data.HasManyAssociation
類和Ext.data.BelongsToAssociation
類實現。
這是官網上的一個例子:
//用戶model 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' } }); //帖子model 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: 'Comment }); //評論model Ext.define('Comment', { extend: 'Ext.data.Model', fields: ['id', 'post_id', 'name', 'message‘ ], belongsTo: 'Post' });
關於讀數據
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')); }); }); } });
load Id爲1的user,而且經過user的proxy load了相關的post和comment。
每個咱們建立的hasMany關係就會有個新的方法添加到這個model。根據name生get方法,否則自動生成默認的model名字小寫加s,user.posts()
,調用該方法,會返回一個配有post model的store,一樣,post得到了一個comments()
方法。
數據大概是這個樣子的:
{ 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' }] }] }] }
關於寫數據
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(); //經過配置好的proxy保存新的記錄
association聯繫不只能幫助咱們讀數據,它對建立新的記錄也有幫助(代碼)咱們建立了一條新的帖子,給了當前user的Id爲user_Id的記錄。調用sync()
方法經過配置好的proxy保存新的記錄,這是異步的操做,我如咱們想在操做成功後作其餘操做能夠在裏面添加回調函數。
一個專利對應多個發明人。
//專利詳細信息model Ext.define('Srims.model.patent.patentDetail', { extend: 'Ext.data.Model', fields: [ { name: 'id', type: 'int'} { name: 'patentName', type: 'string'}, { name: 'patentNumber', type: 'string'}, …… ], hasMany: { model: 'Srims.model.patent.inventors', name: 'Owners', associationKey: 'Owners', foreignKey: 'patentId' } proxy: {/*??*/} }); //發明人model Ext.define('Srims.model.patent.inventors', { extend: 'Ext.data.Model', fields: [ { name: 'Id', type: 'int' }, { name: 'Ordinal', type: 'int' }, { name: 'Name', type: 'string' }, …… { name: 'patentId', type: 'int'} ] belongsTo: 'Srims.model.patent.patents' proxy: {/*??*/} });
URL中都須要參數,那麼proxy如何配置?
Ext.define('Srims.model.patent.patentDetail', { …… proxy: { type: "rest", reader: {type: "json"}, url: 'http://222.195.150.214:24367/api/patents' } }); var patentModel = Ext.ModelManager.getModel('Srims.model.patent.patentDetail'); patentModel.load(3017);
有關發明人的URL中間某個位置須要參數……
添加一條發明人記錄:POST http://222.195.150.214:24367/api/patents/{patentId}/patentInventors
修改一條發明人記錄:PUT http://222.195.150.214:24367/api/patents/{patentId/patentInventors/{ordinal}
Ext.define('Srims.model.patent.inventors', { extend: 'Ext.data.Model', …… proxy: { type: "rest", reader: {type: "json"}, url: 'http://222.195.150.214:24367/api/patents/{}/patentInventors', //重寫buildUrl buildUrl: function(request) { var me = this, operation = request.operation, records = operation.records || [], record = records[0], url = me.getUrl(request), id = record && record.get("patentId"); Ordinal = record && record.get("Ordinal"); if (me.isValidId(id)) { //將{}替換爲patentId url = url.replace('{}', id); } else { throw new Error('A valid id is required'); } if (operation.action === 「update」) { //若爲修改操做,在URL末尾加上位次 url += '/' + Ordinal; } request.url = url; return Ext.data.proxy.Rest.superclass.buildUrl.apply(this, arguments); } } });
讀寫發明人:
var patentModel = Ext.ModelManager.getModel('Srims.model.patent.patentDetail'); patentModel.load(3017, { success: function (record) { record.Owners().add({ Ordinal: 6, Name: "zhanglu" }); record.Owners().getNewRecords()[0].save({ //添加一條記錄 post success: function(){ record.Owners().sync(); //將其餘記錄同步(equal to修改)put } }); } });
presence
保證了字段有值。零是有效的,但空字符串無效。
length
確保了一個string類型的字段長度必須在最大值和最小值之間,兩個值都是可選的。
format
確保字符串必須與正則表達式匹配。
inclusion
確保該字段的值必須在一個特定的集合中。
exclusion
與inclusion
相反,確保該字段的值不在某個特定的集合中。
-
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: ... });