此文中讲到的ember-data已经是老版本,不推荐大家使用了。
由于是从word考过来的,格式不是太好,大家可以直接去下载我的完整word。里面除了翻译还有原创内容。
http://download.csdn.net/detail/kevinwon1985/5230326
在大多数Ember应用中,模型都通过Ember Data处理。Ember Data是一个专门用于框架的库。它设计目的是方便的从个服务端获取数据,在浏览器中改变数据,并将改变保存回服务器。
它提供了很多能在服务端找到的管理ORM的工具,例如活动记录。但是它是专门为了浏览器环境中的javascript设计的。
在遵循某些惯例的情况下,不需要很多配置,Ember Data通过RESTful JSON API能加载数据,保存数据和关系。
每个使用Ember Data的应用都需要有store。Store负责保存已经加载的记录和接收还没加载的记录。
你会与模型直接交互,而不是store。然后,你需要告诉框架你想使用Ember Data管理你的模型。所以,你需要定义一个DS.Store的子类。
App.Store = DS.Store.extend({ revision: 11 }); |
注意这里定义的revision。它是Ember Data API版本号,被Ember Data用来通知你更新。升级了Ember Data后,这个数字会改。它也会在Ember Data的版本到达1.0的时候被移除。
如果你想使用自己的连接器,来代替默认的DS.RESTAdapter。这样写:
App.Store = DS.Store.extend({ revision: 11, adapter:'App.MyCustomAdapter' }); |
3.3.2.1 属性
App.Person = DS.Model.extend({ firstName: DS.attr('string'), lastName: DS.attr('string'), birthday: DS.attr('date'),
fullName:function() { returnthis.get('firstName') + '' +this.get('lastName'); }.property('firstName','lastName') }); |
默认的REST连接器中,支持的属性类型有string,number,boolean,和date。自定义的连接器可以提供额外的属性类型,新属性可以注册为transforms。详见3.3.6。
3.3.2.2 关系
Ember Data 包含数种内置关系类型来帮助你定义模型间的关系。
单对单
App.User = DS.Model.extend({ profile: DS.belongsTo('App.Profile') });
App.Profile = DS.Model.extend({ user: DS.belongsTo('App.User') }); |
单对多
App.Post = DS.Model.extend({ comments: DS.hasMany('App.Comment') });
App.Comment = DS.Model.extend({ post: DS.belongsTo('App.Post') }); |
多对多
App.Post = DS.Model.extend({ tags: DS.hasMany('App.Tag') });
App.Tag = DS.Model.extend({ posts: DS.hasMany('App.Post') }); |
3.3.3.1. 返回一条记录
通过传递唯一的id来查找一条记录用find方法。
var post = App.Post.find(1); |
如果查找的记录已经存在于本地,就会立即返回。这个特性,我们有时叫它身份映射。
否则,会创建一条新的记录,并设置它的状态为loading,然后返回。此时马上就可以在模版中使用这条记录了。因为框架中所有的所有东西都是绑定好的,模版会在这条记录加载完成后自动更新。
3.3.3.2. 查找所有记录
你也可以用使用无参数的find方法查找所有的记录。
var post = App.Post.find(); |
这会返回一个DS.RecordArray的实例。这个recordArray开始是loading状态,length是0,但是你可以在模版中马上用它,像用一个列表一样。当服务端响应并返回结果,模版会自动更新。
注意:DS.RecordArray不是一个Javascript数组,它是一个实现了Ember.Enumerable的对象。如果你想要通过索引访问它的成员,需要用objectAt(index)方法,用[index]是不会起作用的。
3.3.3.3. 检索
你可以通过传递一个hash对象来使用find方法来向服务器检索。
var people = App.Person.find({ name: "Peter" }); |
这个hash对象的具体内容框架并不知道,需要由服务器来解析,并且返回一个模型实例集合集。
只有在一条记录已经加载,你才能修改它的属性。就像修改一个Ember对象一样。
var tyrion = App.Person.find(1); // ...after the record has loaded tyrion.set('firstName',"Yollo"); |
所有框架的便捷修改属性的方法都可以用。例如Ember.Object的incrementProperty。
person.incrementProperty('age'); |
你可以通过检测记录的isDirty属性来判断这条记录的改动是否已经保存。
person.get('isDirty'); //=> false
person.set('isAdmin', true);
person.get('isDirty'); //=> true |
确保记录已经加载,再去修改它的属性。如果在loading状态修改,Ember Data会抛异常。
• isLoaded 连接器已经从后台取回了记录。
• isDirty 在本地修改了记录但是还没通过连接器保存。这也包括新建和删除的记录。
• isSaving 连接器已经向后台发送了保存的请求,但是还没确认保存成功。
• isDeleted 记录被标记为删除。当isDeleted为true,isDirty也为true,该记录只是在本地被删除,服务端还没删除。当isSaving为true,删除请求在发送中。当isDirty和isSaving都是false,改变已经被保存。
• isError 连接器表示不能将本地修改保存到后台。当连接器收到服务端的验证失败通知时,这也可能导致isValid属性变为false。
• isNew 记录已经在本地被创建,但是还没有被连接器成功保存。
• isValid 客户端验证通过,并且连接器没有收到服务端验证失败的通知。
另外,记录状态改变时会产生一些可以被监听的事件。
record.on('didLoad',function() { console.log("Loaded!"); }); |
有以下事件:
• didLoad
• didCreate
• didUpdate
• didDelete
• becameError
• becameInvalid
记录的状态:
• Loading
还没有从服务端接收到属性值和关系。一条记录通常以loading状态开始。
试图修改loading状态的记录会导致一个异常。
• Loaded/clean
同时处于Loaded和clean两个状态意思是,已经从个服务端接收到了属性值和关系,并且本地没有对记录进行任何修改。
• Dirty
记录已经在本地被改变,但是还没有与服务端同步。
• In-flight
dirty状态的记录已经发起了与服务端的同步请求。一旦服务端保存成功,记录状态会被置为clean。
• Invalid
服务端返回通知数据验证不通过而不能保存。
• Error
服务端由于某些不正当的理由而没有保存。
Ember Data默认使用DS.RESTAdapter加载和保存记录。REST连接器假定URL和模型关联的JSON都是按惯例的。如是遵循了那些规则,就不需要配置任何东西。
3.3.6.1. URL约定
连接器基于模型的名字自动生成通讯的URL。例如,通过ID查找 User的一条记录
var user = App.User.find(1); |
REST连接器会自动发送一个GET请求到/users/1。
在REST连接器中,你可以对记录进行的操作映射成下面的URL
操作 | HTTP method | URL |
Find | GET | /users/1 |
Find All | GET | /users |
Update | PUT | /users/1 |
Create | POST | /users |
Delete | DELETE | /users/1 |
3.3.6.1.1. 复数配置
不规则的复数可以通过连接器的configureAPI指定
DS.RESTAdapter.configure("plurals", { person:"people" }); |
这会告诉连接器
App.Person请求的地址会由 /persons/1变成 /people/1。3.3.6.1.2. 路径配置
可以通过url和namespace来配置请求的路劲。默认请求地址是url+namespace+上面的操作地址。
DS.RESTAdapter.reopen({ url:'https://api.example.com',//默认是“” namespace: 'api/1' //默认是undefined }); |
为App.Person请求的地
。3.3.6.2. JSON格式约定
当像后台请求数据,REST连接器期望返回的JSON数据符合以下约定
3.3.6.2.1. JSON ROOT
如果你从/people/123请求一条记录,返回的记录要内嵌在person属性中。
{ "person": { "first_name":"Jeff", "last_name":"Atwood" } } |
3.3.6.2.2. 属性名用下划线连接符
属性名的多个单词需要使用下划线连接。例如,有如下模型
App.Person = DS.Model.extend({ firstName: DS.attr('string'), lastName: DS.attr('string'),
isPersonOfTheYear: DS.attr('boolean') }); |
isPersonOfTheYear的驼峰格式要用下划线代替。返回一条记录的JSON是这样的
{ "person": { "first_name":"Barack", "last_name":"Obama", "is_person_of_the_year": true } } |
不规则的属性名可以通过连接器的map方法进行映射。
3.3.6.2.3. 关系
引用到其他记录要通过id。例如,你有一个hasMany关系的模型。
App.Post = DS.Model.extend({ comments: DS.hasMany('App.Comment') }); |
返回的JSON需要编码关系为一个ids的数组
{ "post": { "comment_ids": [1, 2, 3] } } |
post的comments可以通过post.get('comments')加载。REST连接器会发送GET请求到/comments?ids[]=1&ids[]=2&ids[]=3。
所有belongsTo关系,在JSON中都用模型名字加_id。例如:你有这样一个模型
App.Comment = DS.Model.extend({ post: DS.belongsTo('App.Post') }); |
返回的JSON是这样的
3.3.6.2.4. 偏载关系
为了减少请求数,你可以在返回的JSON中偏载额外的记录。偏载的记录在JSON ROOT之外,并且是一个hash数组。
{ "post": { "id": 1, "title":"Rails is omakase", "comment_ids": [1, 2, 3] },
"comments": [{ "id": 1, "body":"But is it _lightweight_ omakase?" }, { "id": 2, "body":"I for one welcome our new omakase overlords" }, { "id": 3, "body":"Put me on the fast track to a delicious dinner" }] } |
3.3.6.2.5. 自定义属性类型
一些情况下,内置的属性类型string, number, boolean, 和 date不够用。例如,服务器返回非标准的日期格式。
Ember的连接器都可以自定义属性类型:
DS.RESTAdapter.registerTransform('coordinatePoint', { serialize:function(value) { return [value.get('x'), value.get('y')]; }, deserialize:function(value) { return Ember.create({x: value[0],y: value[1] }); } }); App.Cursor = DS.Model.extend({ position: DS.attr('coordinatePoint') }); |
position返回的是一个数组
{ cursor: { position: [4,9] } } |
但是加载后,它是一个对象:
var cursor = App.Cursor.find(1); cursor.get('position.x');//=> 4 cursor.get('position.y');//=> 9 |