Ember.js 入門指南——自定義序列化器

       Ember應用中,序列化器會格式化與後臺交互的數據,包括髮送和接收的數據。默認狀況下會使用JSON API序列化數據。若是你的後端使用不一樣的格式,Ember Data容許你自定義序列化器或者定義一個徹底不一樣的序列化器。html

       Ember Data內置了三個序列化器。JSONAPISerializer是默認的序列化器,用與處理後端的JSON APIJSONSerializer是一個簡單的序列化器,用與處理單個JSON對象或者是處理記錄數組。RESTSerializer是一個複雜的序列化器,支持側面加載,在Ember Data2.0以前是默認的序列化器。前端

 

JSONAPISerializer規範

       當你向服務器請求數據時,JSONSerializer會把服務器返回的數據當作是符合下列規範的JSON數據。git

1JSON API文檔

       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"
           }
       }
  ]
}

 

2,拷貝數據

       數據有時候並非請求的主體,若是數據有連接。連接的關係會放在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數據看出,id5comment連接是"self": http://example.com/comments/5id12comment連接是"self": http://example.com/comments/12。而且這些連接是單獨放置included內。數組

 

3,自定義序列化器

       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"
  }
}

 

此時你能夠重寫回調方法normalizeResponsenormalize,在方法裏設置數據格式:

//  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);
       }
});

       有關更多自定義序列化器請移步官網文檔

 

4,數據ID屬性

       每一條數據都有一個惟一值做爲ID,默認狀況下Ember會爲每一個model加上一個名爲id的屬性。若是你想改成其餘名稱,你能夠在序列化器中指定。

//  app/serializers/application.js
 
import DS from 'ember-data';
 
export default DS.JSONSerializer.extend({
       primatyKey: '__id'
});

       把數據主鍵名修改成「__id」

 

5,屬性名

       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); 
  }
});

6,指定屬性名的別名

       若是你想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

 

7model之間的關聯關係

       一個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" },
      }
    }
  }
}

ID1comment關聯了ID5post

 

8,自定義轉換規則

       在某些狀況下,Ember內置的屬性類型(stringnumberbooleandate)仍是不夠用的。好比,服務器返回的是非標準的數據格式時。

       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

 

9JSONSerializer

       並非全部的API都遵循JSONAPISerializer約定經過數據命名空間和拷貝關係記錄。好比系統遺留問題,原先的API返回的只是簡單的JSON格式並非JSONAPISerializer約定的格式,此時你能夠自定義序列化器去適配舊接口。而且能夠同時兼容使用RESTAdapter去序列號這些簡單的JSON數據。

//  app/serializer/application.js
 
export default DS.JSONSerializer.extend({
  // ...
});

 

10EMBEDDEDRECORDMIXIN

       儘管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個關鍵字:

  1. records 用於標記所有的記錄都是序列化與反序列化的

  2. ids 用於標記僅僅序列化與反序列化記錄的id

  3. false 用於標記記錄不須要序列化與反序列化

 

例如,你可能會發現你想讀一個嵌入式記錄提取時一個JSON有效載荷只包括關係的身份在序列化記錄。這多是使用serialize: ids。你也能夠選擇經過設置序列化的關係 serialize: false

export default DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin, {
  attrs: {
    author: {
      serialize: false,
      deserialize: 'records'
    },
    comments: {
      deserialize: 'records',
      serialize: 'ids'
    }
  }
});

11EMBEDDEDRECORDSMIXIN 默認設置

       若是你沒有重寫attrs去指定model的關聯關係,那麼EmbeddedRecordsMixin會有以下的默認行爲:

belongsTo{serialize: ‘id’, deserialize: ‘id’ }

hasMany: { serialize: false, deserialize: ‘ids’ }

 

12,創做序列化器

       若是項目須要自定義序列化器,Ember推薦擴展JSONAIPSerializer或者JSONSerializer來實現你的需求。可是,若是你想徹底建立一個全新的與JSONAIPSerializerJSONSerializer都不同的序列化器你能夠擴展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記錄。

 

更多有關於序列化器的介紹,請看下面的網址:

·       Ember Observer

·       GitHub

·       Bower

 

本篇的內容難度很大,而且也很差作演示例子,本身也理解不到位!!!但願能在後面深刻學習Ember以後回來補充完整……

 

 

       到本篇爲止,有關Ember的基礎知識所有介紹完畢!!!從2015-08-26開始到恰好2個月,原計劃是用3個月時間完成的,提早了一個月,歸其緣由是後面的內容難度大,理解誤差大!文章質量也很差,感受時間比較倉促,說以節省了不少時間!

       介紹來打算介紹APPLICATION CONCERNSTESTING這兩章!也有可能把舊版的Ember todomvc案例改爲Ember2.0版本的,正好能夠拿來練練手速!!!

相關文章
相關標籤/搜索