前面在《Ember.js 项目开发之 Ember Data》介绍了Ember Data 和 《Ember Data 之模型定义》,本文将继续介绍记录操作。
记录检索
Ember Data 存储提供了一个接口,用于检索单一类型的记录。
检索单个记录
使用 store.findRecord()
是根据记录的类型和ID进行检索,通过返回一个 promise
处理满足检索条件的记录:
// GET /blog-posts/1 this.store .findRecord("blog-post", 1) // => GET /blog-posts/1 .then(function (blogPost) { // 解析数据 });
使用 store.peekRecord()
是根据记录的类型和ID进行,不会发出HTTP请求,只有当记录已经存在于 store 中时,才会被返回:
const blogPost = this.store.peekRecord("blog-post", 1); // => 没有HTTP请求
检索多个记录
使用 store.findAll()
检索指定类型的所有记录:
// GET /blog-posts this.store .findAll("blog-post") // => GET /blog-posts .then(function (blogPosts) { // 解析记录数据 });
使用store.peekall()
来检索已经加载到 store
中的指定类型的所有记录,而不需要发出HTTP请求:
const blogPosts = this.store.peekAll("blog-post"); // => 没有HTTP请求
store.findAll()
返回一个满足条件的RecordArray
和 store
的 PromiseArray
,store.peekAll
则直接返回 RecordArray
。
需要注意的是
RecordArray
并不是一个 JavaScript 数组,它是一个实现MutableArray
的对象。这是很重要的,因为,例如,如果想按索引检索记录,[]
符号将不起作用,必须使用objectAt(index)
代替。
查询多条记录
Ember Data 提供了查询满足特定条件的记录的功能。调用 store.query()
将发出一个 GET
请求,并将传递的对象序列化为查询参数。这个方法与 findAll
一样返回一个PromiseArray
。
例如,下面在 person
模型中检索名字为 Peter
的人:
// GET to /persons?filter[name]=Peter this.store .query("person", { filter: { name: "Peter", }, }) .then(function (peters) { // 解析检索到的记录 });
查询单条记录
如果使用的适配器支持能够返回单个模型对象的服务器请求,那么 Ember Data 提供了一种便捷的方法 store.queryRecord()
,它将返回一个解析该单个记录的 promise
。请求是通过适配器定义的 queryRecord()
方法发出的。
例如,如果服务器 API 为当前登录的用户提供了一个端点app/adapters/user.js
:
// GET /api/current_user { user: { id: 6188, username: "devpoint", }, };
如果 User
模型的适配器定义了一个针对该端点的 queryRecord()
方法:
import Adapter from "@ember-data/adapter"; import fetch from "fetch"; export default class UserAdapter extends Adapter { queryRecord(store, type, query) { return fetch("/api/current_user"); } }
然后,调用 store.queryRecord()
将从服务器检索该对象:
store.queryRecord("user", {}).then(function (user) { const username = user.get("username"); console.log(`Currently logged in as ${username}`); });
与 store.query()
的情况一样,查询对象也可以传递给 store.queryRecord()
,适配器的 queryRecord()
可以使用查询对象来限定请求。但是,适配器必须返回单个模型对象,而不是包含一个元素的数组,否则 Ember Data
将抛出异常。
需要注意,Ember 的默认
JSON:API adapter
没有提供直接支持queryRecord()
所需的方法,因为它依赖于以数组形式返回结果数据的 REST 请求定义。
如果服务器 API 或适配器仅提供数组响应,但又希望只检索一条记录,也可以使用 query()
方法,如下所示:
如果服务器API或适配器仅提供数组响应,但是希望仅检索单个记录,则可以选择使用以下query()
方法:
// GET to /users?filter[email]=quintiontang@gmail.com const devpoint = store .query("user", { filter: { email: "quintiontang@gmail.com", }, }) .then(function (users) { return users.get("firstObject"); });
创建记录
可以通过调用Store中的createRecord()
方法来创建记录 。
store.createRecord('post', { title: 'Rails is Omakase', body: 'Lorem ipsum' });
可以在控制器和路由中使用store对象this.store
。
更新记录
更改Ember Data记录就像设置要更改的属性一样简单:
this.store.findRecord('person', 1).then(function(tyrion) { // ...after the record has loaded tyrion.firstName = 'Yollo'; });
持续记录
Ember Data中的记录将按实例持久保存。调用 save()
的任何实例,Model将发出网络请求。
Ember Data会为跟踪每个记录的状态。保存时,这使Ember Data可以将新创建的记录与现有记录区别对待。
默认情况下,Ember Data将POST新创建的记录添加到其类型URL。
let post = store.createRecord('post', { title: 'Rails is Omakase', body: 'Lorem ipsum' }); post.save(); // => POST to '/posts'
后端已存在的记录使用HTTP PATCH
动词更新。
store.findRecord('post', 1).then(function(post) { post.title; // => "Rails is Omakase" post.title = 'A new post'; post.save(); // => PATCH to '/posts/1' });
可以通过检查记录的hasDirtyAttributes
属性来判断记录是否有尚未保存的未完成的更改。还可以使用changedAttributes()
方法查看记录的哪些部分被更改了,以及原始值是什么。changedAttributes
返回一个对象,该对象的键是已更改的属性,值是一个值数组[oldValue, newValue]
。
person.isAdmin; // => false person.hasDirtyAttributes; // => false person.isAdmin = true; person.hasDirtyAttributes; // => true person.changedAttributes(); // => { isAdmin: [false, true] }
此时,可以通过保留更改,save()
也可以回滚更改。调用 rollbackAttributes()
保存的记录将全部恢复changedAttributes
为原始值。如果有记录isNew
,它将从Store中删除。
person.hasDirtyAttributes; // => true person.changedAttributes(); // => { isAdmin: [false, true] } person.rollbackAttributes(); person.hasDirtyAttributes; // => false person.isAdmin; // => false person.changedAttributes(); // => {}
处理Validation错误
如果后端服务器在尝试保存后返回验证错误,则这些错误将在errors模型的属性上可用。这是在博客模板中保存博客帖子时可能显示的错误的方式:
{{#each this.post.errors.title as |error|}} <div class="error">{{error.message}}</div> {{/each}} {{#each this.post.errors.body as |error|}} <div class="error">{{error.message}}</div> {{/each}}
Promises
save()
返回一个promise,这使得异步处理成功和失败方案变得容易。这是一个常见的模式:
let post = store.createRecord('post', { title: 'Rails is Omakase', body: 'Lorem ipsum' }); let self = this; function transitionToPost(post) { self.transitionToRoute('posts.show', post); } function failure(reason) { // handle the error } post .save() .then(transitionToPost) .catch(failure); // => POST to '/posts' // => transitioning to posts.show route
删除记录
删除记录与创建记录一样简单。在模型的任何实例上调用deleteRecord()
。这将记录标记为已删除。然后可以使用save()
持久化删除。或者,可以使用destroyRecord
方法同时删除和持久化。
let post = store.peekRecord('post', 1); post.deleteRecord(); post.isDeleted; // => true post.save(); // => DELETE to /posts/1 }); // OR post = store.peekRecord('post', 2); post.destroyRecord(); // => DELETE to /posts/2 });