在Ember應用中,序列化器會格式化與後臺交互的數據,包括髮送和接收的數據。默認狀況下會使用JSON API序列化數據。若是你的後端使用不一樣的格式,Ember Data容許你自定義序列化器或者定義一個徹底不一樣的序列化器。html
Ember Data內置了三個序列化器。JSONAPISerializer是默認的序列化器,用與處理後端的JSON API。JSONSerializer是一個簡單的序列化器,用與處理單個JSON對象或者是處理記錄數組。RESTSerializer是一個複雜的序列化器,支持側面加載,在Ember Data2.0以前是默認的序列化器。前端
當你向服務器請求數據時,JSONSerializer會把服務器返回的數據當作是符合下列規範的JSON數據。git
JSONSerializer期待後臺返回的是一個符合JSON API規範和約定的JSON文檔。好比下面的JSON數據,這些數據的格式是這樣的:github
1,type指定model的名稱json
2,屬性名稱使用中劃線分隔ubuntu
好比請求/people/123,響應的數據以下:vim
{ "data": { "type": "people", "id": "123", "attributes": { "first-name": "Jeff", "last-name": "Atwood" } } }
若是響應的數據有多條,那麼data將是以數組形式返回。後端
{ "data": [ { "type": "people", "id": "123", "attributes": { "first-name": "Jeff", "last-name": "Atwood" } },{ "type": "people", "id": "124", "attributes": { "first-name": "chen", "last-name": "ubuntuvim" } } ] }
數據有時候並非請求的主體,若是數據有連接。連接的關係會放在included下面。api
{ "data": { "type": "articles", "id": "1", "attributes": { "title": "JSON API paints my bikeshed!" }, "links": { "self": "http://example.com/articles/1" }, "relationships": { "comments": { "data": [ { "type": "comments", "id": "5" }, { "type": "comments", "id": "12" } ] } } }], "included": [{ "type": "comments", "id": "5", "attributes": { "body": "First!" }, "links": { "self": "http://example.com/comments/5" } }, { "type": "comments", "id": "12", "attributes": { "body": "I like XML better" }, "links": { "self": "http://example.com/comments/12" } }] }
從JSON數據看出,id爲5的comment連接是"self": http://example.com/comments/5。id爲12的comment連接是"self": http://example.com/comments/12。而且這些連接是單獨放置included內。數組
Ember Data默認的序列化器是JSONAPISerializer,可是你也能夠自定義序列化器覆蓋默認的序列化器。
要自定義序列化器首先要定義一個名爲application序列化器做爲入口。
直接使用命令生成:ember g serializer application
// app/serializers/application.js import DS from 'ember-data'; export default DS.JSONSerializer.extend({ });
甚至你還能夠針對某個model定義序列化器。好比下面的代碼爲post定義了一個專門的序列化器。
// app/serializers/post.js import DS from ‘ember-data’; export default DS.JSONSerializer.extend({ });
若是你想改變發送到後端的JSON數據格式,你只需重寫serialize回調,在回調中設置數據格式。
好比前端發送的數據格式是以下結構,
{ "data": { "attributes": { "id": "1", "name": "My Product", "amount": 100, "currency": "SEK" }, "type": "product" } }
可是服務器接受的數據結構是下面這種結構:
{ "data": { "attributes": { "id": "1", "name": "My Product", "cost": { "amount": 100, "currency": "SEK" } }, "type": "product" } }
此時你能夠重寫serialize回調。
// app/serializers/application.js import DS from 'ember-data'; export default DS.JSONSerializer.extend({ serialize: function(snapshot, options) { var json = this._super(...arguments); // ?? json.data.attributes.cost = { amount: json.data.attributes.amount, currency: json.data.attributes.currency }; delete json.data.attributes.amount; delete json.data.attributes.currency; return json; } });
那麼若是是反過來呢。
若是後端返回的數據格式爲:
{ "data": { "attributes": { "id": "1", "name": "My Product", "cost": { "amount": 100, "currency": "SEK" } }, "type": "product" } }
可是前端須要的格式是:
{ "data": { "attributes": { "id": "1", "name": "My Product", "amount": 100, "currency": "SEK" }, "type": "product" } }
此時你能夠重寫回調方法normalizeResponse或normalize,在方法裏設置數據格式:
// app/serializers/application.js import DS from 'ember-data'; export default DS.JSONSerializer.extend({ normalizeResponse: function(store, primaryModelClass, payload, id, requestType) { payload.data.attributes.amount = payload.data.attributes.cost.amount; payload.data.attributes.currency = payload.data.attributes.cost.currency; delete payload.data.attributes.cost; return this._super(...arguments); } });
有關更多自定義序列化器請移步官網文檔。
每一條數據都有一個惟一值做爲ID,默認狀況下Ember會爲每一個model加上一個名爲id的屬性。若是你想改成其餘名稱,你能夠在序列化器中指定。
// app/serializers/application.js import DS from 'ember-data'; export default DS.JSONSerializer.extend({ primatyKey: '__id' });
把數據主鍵名修改成「__id」。
Ember Data約定的屬性名是駝峯式的命名方式,可是序列化器卻指望的是中劃線分隔的命名方式,不過Ember會自動轉換,不須要開發者手動指定。然而,若是你想修改這種默認的方式也是能夠的,只需在序列化器中使用屬性keyForAttributes指定你喜歡的分隔方式便可。好比下面的代碼把序列號的屬性名稱改成如下劃線分隔:
// app/serializers/application.js import DS from 'ember-data'; export default DS.JSONSerializer.extend({ keyForAttributes: function(attr) { return Ember.String.underscore(attr); } });
若是你想model數據被序列化、反序列化時指定model屬性的別名,直接在序列化器中使用attrs屬性指定便可。
// app/models/person.js export default DS.Model.extend({ lastName: DS.attr(‘string’) });
指定序列化、反序列化屬性別名:
// app/serializers/application.js import DS from 'ember-data'; export default DS.JSONSerializer.extend({ attrs: { lastName: ‘lastNameOfPerson’ } });
指定model屬性別名爲lastNameOfPerson。
一個model經過ID引用另外一個model。好比有兩個model存在一對多關係:
// app/models/post.js export default DS.Model.extend({ comments: DS.hasMany(‘comment’, { async: true }); });
序列化後JSON數據格式以下,其中關聯關係經過一個存放ID屬性值的數組實現。
{ "data": { "type": "posts", "id": "1", "relationships": { "comments": { "data": [ { "type": "comments", "id": "5" }, { "type": "comments", "id": "12" } ] } } } }
可見,有兩個comment關聯到一個post上。
若是是belongsTo關係的,JSON結構與hadMany關係相差不大。
{ "data": { "type": "comment", "id": "1", "relationships": { "original-post": { "data": { "type": "post", "id": "5" }, } } } }
ID爲1的comment關聯了ID爲5的post。
在某些狀況下,Ember內置的屬性類型(string、number、boolean、date)仍是不夠用的。好比,服務器返回的是非標準的數據格式時。
Ember Data能夠註冊新的JSON轉換器去格式化數據,可用直接使用命令建立:ember g transform coordinate-point
// app/transforms/coordinate-point.js import DS from 'ember-data'; export default DS.Transform.extend({ deserialize: function(v) { return [v.get('x'), v.get('y')]; }, serialize: function(v) { return Ember.create({ x: v[0], y: v[1]}); } });
定義一個複合屬性類型,這個類型由兩個屬性構成,造成一個座標。
// app/models/curor.js import DS from 'ember-data'; export default DS.Model.extend({ position: DS.attr(‘coordinate-point’) });
自定義的屬性類型使用方式與普通類型一致,直接做爲attr方法的參數。最後當咱們接受到服務返回的數據形以下面的代碼所示:
{ cursor: { position: [4, 9] } }
加載model實例時仍然做爲一個普通對象加載。仍然可使用「.」獲取屬性值。
var cursor = this.store.findRecord(‘cursor’, 1); cursor.get(‘position.x’); // => 4 cursor.get(‘position.y’); // => 9
9,JSONSerializer
並非全部的API都遵循JSONAPISerializer約定經過數據命名空間和拷貝關係記錄。好比系統遺留問題,原先的API返回的只是簡單的JSON格式並非JSONAPISerializer約定的格式,此時你能夠自定義序列化器去適配舊接口。而且能夠同時兼容使用RESTAdapter去序列號這些簡單的JSON數據。
// app/serializer/application.js export default DS.JSONSerializer.extend({ // ... });
儘管Ember Data鼓勵你拷貝model關聯關係,但有時候在處理遺留API時,你會發現你須要處理的JSON中嵌入了其餘model的關聯關係。不過EmbeddedRecordsMixin能夠幫你解決這個問題。
好比post中包含了一個author記錄。
{ "id": "1", "title": "Rails is omakase", "tag": "rails", "authors": [ { "id": "2", "name": "Steve" } ] }
你能夠定義裏的model關聯關係以下:
// app/serializers/post.js export default DS.JSONSerialier.extend(DS.EmbeddedRecordsMixin, { attrs: { author: { serialize: ‘records’, deserialize: ‘records’ } } });
若是你發生對象自己須要序列化與反序列化嵌入的關係,你可使用屬性embedded設置。
// app/serializers/post.js export default DS.JSONSerialier.extend(DS.EmbeddedRecordsMixin, { attrs: { author: { embedded: ‘always’ } } });
序列化與反序列化設置有3個關鍵字:
records 用於標記所有的記錄都是序列化與反序列化的
ids 用於標記僅僅序列化與反序列化記錄的id
false 用於標記記錄不須要序列化與反序列化
例如,你可能會發現你想讀一個嵌入式記錄提取時一個JSON有效載荷只包括關係的身份在序列化記錄。這多是使用serialize: ids。你也能夠選擇經過設置序列化的關係 serialize: false。
export default DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin, { attrs: { author: { serialize: false, deserialize: 'records' }, comments: { deserialize: 'records', serialize: 'ids' } } });
若是你沒有重寫attrs去指定model的關聯關係,那麼EmbeddedRecordsMixin會有以下的默認行爲:
belongsTo:{serialize: ‘id’, deserialize: ‘id’ }
hasMany: { serialize: false, deserialize: ‘ids’ }
若是項目須要自定義序列化器,Ember推薦擴展JSONAIPSerializer或者JSONSerializer來實現你的需求。可是,若是你想徹底建立一個全新的與JSONAIPSerializer、JSONSerializer都不同的序列化器你能夠擴展DS.Serializer類,可是你必需要實現下面三個方法:
知道規範化JSON數據對Ember Data來講是很是重要的,若是model屬性名不符合Ember Data規範這些屬性值將不會自動更新。若是返回的數據沒有在model中指定那麼這些數據將會被忽略。好比下面的model定義,this.store.push()方法接受的格式爲第二段代碼所示。
// app/models/post.js import DS from 'ember-data'; export default DS.Model.extend({ title: DS.attr(‘string’), tag: DS.attr(‘string’), comments: hasMany(‘comment’, { async: true }), relatedPosts: hasMany(‘post’) });
{ data: { id: "1", type: 'post', attributes: { title: "Rails is omakase", tag: "rails", }, relationships: { comments: { data: [{ id: "1", type: 'comment' }, { id: "2", type: 'comment' }], }, relatedPosts: { data: { related: "/api/v1/posts/1/related-posts/" } } } }
每一個序列化記錄必須按照這個格式要正確地轉換成Ember Data記錄。
更多有關於序列化器的介紹,請看下面的網址:
· GitHub
· Bower
本篇的內容難度很大,而且也很差作演示例子,本身也理解不到位!!!但願能在後面深刻學習Ember以後回來補充完整……
到本篇爲止,有關Ember的基礎知識所有介紹完畢!!!從2015-08-26開始到現在恰好2個月,原計劃是用3個月時間完成的,提早了一個月,歸其緣由是後面的內容難度大,理解誤差大!文章質量也很差,感受時間比較倉促,說以節省了不少時間!
介紹來打算介紹APPLICATION CONCERNS和TESTING這兩章!也有可能把舊版的Ember todomvc案例改爲Ember2.0版本的,正好能夠拿來練練手速!!!