您的位置:首页 > Web前端 > JavaScript

Ember.js 入门指南——自定义序列号器

2015-10-25 02:21 866 查看
       在Ember应用中,序列化器会格式化与后台交互的数据,包括发送和接收的数据。默认情况下会使用JSON API序列化数据。如果你的后端使用不同的格式,Ember Data允许你自定义序列化器或者定义一个完全不同的序列化器。

       Ember Data内置了三个序列化器。JSONAPISerializer是默认的序列化器,用与处理后端的JSON API。JSONSerializer是一个简单的序列化器,用与处理单个JSON对象或者是处理记录数组。RESTSerializer是一个复杂的序列化器,支持侧面加载,在Ember
Data2.0之前是默认的序列化器。

 


JSONAPISerializer规范

       当你向服务器请求数据时,JSONSerializer会把服务器返回的数据当做是符合下列规范的JSON数据。

1,JSON API文档


       JSONSerializer期待后台返回的是一个符合JSON API规范和约定的JSON文档。比如下面的JSON数据,这些数据的格式是这样的:

1,type指定model的名称

2,属性名称使用中划线分隔

比如请求/people/123,响应的数据如下:
{
  "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下面。
{
  "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内。

 

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


 

此时你可以重写回调方法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);
       }
});


       有关更多自定义序列化器请移步官网文档

 

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。

 

7,model之间的关联关系


       一个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。

 

8,自定义转换规则


       在某些情况下,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({
// ...
});


 

10,EMBEDDEDRECORDMIXIN


       尽管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'
}
}
});


11,EMBEDDEDRECORDSMIXIN 默认设置


       如果你没有重写attrs去指定model的关联关系,那么EmbeddedRecordsMixin会有如下的默认行为:

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

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

 

12,创作序列化器


       如果项目需要自定义序列化器,Ember推荐扩展JSONAIPSerializer或者JSONSerializer来实现你的需求。但是,如果你想完全创建一个全新的与JSONAIPSerializer、JSONSerializer都不一样的序列化器你可以扩展DS.Serializer类,但是你必须要实现下面三个方法:

normalizeResponse

serialize

normalize

知道规范化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 CONCERNS和TESTING这两章!也有可能把旧版的Ember todomvc案例改成Ember2.0版本的,正好可以拿来练练手速!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Ember.js JavaScript