呃……前面那个坑还没填完,因为我又改变了一些想法还在试验中。
这是一个小坑,是两个月前给新团队的小伙伴们入门 Ember Data 时写的大纲,这两个月下来我发现还是挺有用的,先放出来以备后面挖大坑用……注意,这份大纲编写的时候 Ember Data 才刚刚 2.0 beta,到今天已经有了一些变化,而且官方的 API 文档做了很多补全或修正,所以实际应用还是要多看文档,以它为准。
DS.StoreDS.Model 的数据实体,为的是让这样的数据实体可以和 Handlebars 模版进行数据绑定DS.ModelDS.InternalModel,专门用来处理 Ember Data 内部的数据模型,但不应用来开发 Ember 应用里的数据模型。这是因为 DS.Model 产生的数据实体是有数据绑定等能力的,适合于 UI 编程(与模版视图进行数据绑定);而 DS.InternalModel 处理的数据实体都是纯 JavaScript 对象,只适合 Ember Data 内部(不需要和模版视图进行数据绑定)。也因此,DS.InternalModel 要比 DS.Model 快些DS.SnapshotDS.RootStatecurrentState 属性,它显式的追踪着数据实体在任意时间点下的状态。比如说一条刚创建并且没有保存(即提交至后端)的数据实体,其状态是 root.loaded.created.uncommitted 等等currentState 属性,数据实体对这些事件如何反应取决于当前所处的状态。在不同的状态下,一些事件是无效的并且会抛出相应的异常(可用来追踪非法操作,比如说标注为 删除状态 的数据实体不能不访问或修改等等)RootState 的子状态。比方说,一条数据实体可以从 root.deleted.uncommitted 状态转换至 root.deleted.inFlight 状态等等。如果一个子状态没有响应的事件回调函数,状态管理会尝试去调用它所有的父级状态的事件回调函数来处理它,直到根级状态。stateName 属性来获取当前状态的完整路径。例如:record.get('currentState.stateName')
DS.Model 的状态其本身是“无状态的”,这个意思是说:分层式的状态数据结构在全局中只有一份(类似单例对象),每一个状态所指向的都是这个不可改变且全局共享的数据结构(immutable and global shared data structure)一个状态可能实现零或多个事件和旗标
transitionTo 方法来完成为了便于理解,举一个例子来说明,想象当前有这样一个状态结构:
接着,如果调用 record.transitionTo('inFlight'),那么状态将会转换至 created.inFlight;如果调用 record.transitionTo('updated.inFlight'),那么状态会转换至 updated.inFlight。
切记!状态转换必须发生在事件回调函数内,永远不要在事件回调函数外调用 transitionTo 方法;如果你确实需要,就创建一个新的自定义事件并发送给状态管理机制(于是这个事件的回调函数就能用来调用 transitionTo 方法了)
旗标就是一系列的布尔类型的属性,为了让你方便的查询数据实体当前的状态(而不是解析路径字符串)。比方说,虽然你可以这样做:
let statePath = record.get('stateManager.currentPath')
if (statePath === 'created.inFlight') {
doSomething()
}
但是,这样做会更方便:
if (record.get('isNew') && record.get('isSaving')) {
doSomething()
}
以上例子也暗示了一件事:旗标与状态并非一一对应的(虽然有时候是),一个状态可能是由多个旗标共同定义的。
如果一个状态没有设置对应的旗标,那么旗标的值会继承父级状态对应的旗标,或者是之前的状态设置的值。
以下是默认的旗标,它们的说明可以在 DS.Model 的文档里找到。如果你需要定义新的旗标,需要在 DS.Model 里定义新的属性。
DS.Transform根据官方文档的描述,Ember Data 默认允许我们定义四种数据类型的数据属性,它们分别是:string number boolean 和 date。有时候我们需要自定义类型的数据属性,我们可以利用 DS.Transform 接口来进行扩展。它的简单用法如下:
// app/transforms/temperature.js
import DS from 'ember-data'
// 转换 JSON 里的摄氏度数,变成应用里的华氏度数
export default DS.Transform.extend({
deserialize: function(serialized) {
return (serialized * 1.8) + 32
},
serialize: function(deserialized) {
return (deserialized - 32) / 1.8
}
})
// app/models/requirement.js
import DS from 'ember-data'
export default DS.Model.extend({
name: DS.attr('string'),
temperature: DS.attr('temperature') // 实际应用
})
DS.AdapterfindRecord()createRecord()updateRecord()deleteRecord()findAll()query()findMany() 方法来优化你的 AdapterDS.RESTAdapter,适用于标准的 REST API Service,你可以重载其中的方法来适应你的(非标准的)REST API ServiceDS.JSONAPIAdapter,JSON API 规范 是基于 REST 架构的新一代的标准,它定义和规范了开发基于 JSON 的 REST API 的一系列标准和实践准则DS.DebugAdapter,这是 Ember Data 内部用于调试的适配器,通常不需要考虑它DS.BuildURLMixin 和 DS.EmbeddedRecordsMixin
这两个 Mixins 用于为 Adapter 实现两种功能:
具体用法可以参考 DS.RESTAdapter 或 DS.JSONAPIAdapter。
DS.SerializernormalizeResponse()serialize()normalize()(可选)DS.JSONSerializer,以及对应 REST 和 JSON API 适配器的 DS.RESTSerializer 和 DS.JSONAPISerializer。需要定义适合自己 API Service 的序列器就可以参考以上的例子或者直接继承 DS.JSONSerializer
举一个常见的例子,假设你有这样的 data model:
App.User = DS.Model.extend({
name: DS.attr(),
friends: DS.hasMany('user'),
house: DS.belongsTo('location'),
})
此时如果直接使用 DS.JSONSerializer,那么请求或响应获得的 JSON 数据应该是这样的:
{
id: 1,
name: 'Sebastian',
friends: [3, 4],
links: {
house: '/houses/lefkada'
}
}
这是很常规的 JSON 数据格式,如果你的服务器与此不符,那么你可以扩展 DS.JSONSerializer 来重新定义。
DS.ErrorsDS.Errors 是 Ember Data 提供的异常和错误处理的基础类DS.Model 都有一个 errors 属性,其值就是一个 DS.Errors 的实例对象,可以用来显示服务端返回的验证错误信息DS.ActiveModelAdapter(以前版本里自带的适配器,用于适配 Rails 框架,现已被抽取出去变成了独立的 Addon)实现了这一套机制,可以自动处理 Rails 返回的错误信息。如果你要定义适合自己 API Service 的错误信息,可以参考它的实现DS.Errors 以期提供更多的默认类,文档尚未补全