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@
const devpoint = store
.query("user", {
filter: {
email: "quintiontang@",
},
})
.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
});