首页 > 网站 > WEB开发 > 正文

EmberJs之使用Ember-Data

2024-04-27 14:09:38
字体:
来源:转载
供稿:网友

EmberJs之使用Ember-Data

写在前面

最近比较忙,换了新工作还要学习很多全新的技术栈,并给自己找了很多借口来不去坚持写博客。常常具有讽刺意味的是,更多剩下的时间并没有利用而更多的是白白浪费,也许这就是青春吧,挥霍吧,这不是我想要的,既然这样,我还要继续写下去,坚持把博客做好,争取进前100博客,在此谨记。

2015年5月7日深夜,于电脑旁。

文章索引

JS前端框架之Ember.js系列

定义模型

Ember-data中所有的模型都继承自DS.Model,模型内部的属性是通过DS.attr来声明的。

var attr = DS.attr;App.Person = DS.Model.extend({  firstName: attr(),  lastName: attr(),  birthday: attr()});

此外,DS.Model中还可以添加“计算属性”:

App.Person = Ember.Object.extend({  // these will be supplied by `create`  firstName: null,  lastName: null,   fullName: function() {    return this.get('firstName') + ' ' + this.get('lastName');  }.PRoperty('firstName', 'lastName')}); var ironMan = App.Person.create({  firstName: "Tony",  lastName:  "Stark"}); ironMan.get('fullName') // "Tony Stark"

特别的,还可以显式指定属性返回值类型:

App.Person = DS.Model.extend({  birthday: DS.attr('date')});

默认情况下,REST 适配器支持的属性类型有string,number,booleandate。 传统的适配器会提供额外的属性类型,并支持你注册自定义的属性类型。 详情请查看documentation section on the REST Adapter。

请注意:date类型参考ISO8601,例如:2014-05-27T12:54:01

选项

目前,Ember-data只支持一种默认的选项:

export default DS.Model.extend({      username: DS.attr('string'),      email: DS.attr('string'),      verified: DS.attr('boolean', {defaultValue: false}),      createdAt: DS.attr('string', {          defaultValue: function() { return new Date(); }      })  });

定义关联模型

Ember Data 包括了几个内置的关联类型,以帮助你确定你的模型如何相互关联的。

理解关联模型

一对一

使用DS.belongsTo在两个模型间声明一对一的关系。

App.User = DS.Model.extend({  profile: DS.belongsTo('profile')}); App.Profile = DS.Model.extend({  user: DS.belongsTo('user')});

一对多

使用DS.belongsTo结合DS.hasMany来声明两个模型间的一对多关系,示例如下:

App.Post = DS.Model.extend({  comments: DS.hasMany('comment')}); App.Comment = DS.Model.extend({  post: DS.belongsTo('post')});

多对多

使用DS.hasMany来声明两个模型间的多对多关系。

App.Post = DS.Model.extend({  tags: DS.hasMany('tag')}); App.Tag = DS.Model.extend({  posts: DS.hasMany('post')});

显式反转

Ember Data会尽最大努力去自动发现关联关系的映射关系。在上例的一对多的情况下,修改了comments会自动更新post,应为这是唯一的一个关联模型。

但是,有时候对同一个类型有多个belongsTo/hasMany关联关系。这时可以通过指定在反向端使用DS.hasManyinverse选项来指定其关联的模型:

var belongsTo = DS.belongsTo,    hasMany = DS.hasMany; App.Comment = DS.Model.extend({  onePost: belongsTo("post"),  twoPost: belongsTo("post"),  redPost: belongsTo("post"),  bluePost: belongsTo("post")}); App.Post = DS.Model.extend({  comments: hasMany('comment', {    inverse: 'redPost'  })});

当然也可以在belongsTo一侧指定,它将按照预期那样工作。

创建和删除记录

创建

通过调用仓库的createRecord方法,可以创建记录:

store.createRecord('post', {  title: 'Rails is Omakase',  body: 'Lorem ipsum'});

尽管createRecord的使用已经非常直接,但是还需要注意一点,就是目前还不支持将一个承诺赋值给一个关联。

例如,如果希望给文章设置author属性,如果指定ID的user并没有加载到仓库中的话,下面的代码将不会正常工作。

var store = this.store; store.createRecord('post', {  title: 'Rails is Omakase',  body: 'Lorem ipsum',  author: store.find('user', 1)});

不过在承诺履行时可以非常方便的进行关联关系的设置:

var store = this.store; var post = store.createRecord('post', {  title: 'Rails is Omakase',  body: 'Lorem ipsum'}); store.find('user', 1).then(function(user) {  post.set('author', user);});

删除

删除记录与创建记录一样简单。只需要调用DS.Model实例的deleteRecord()方法即可。这将会吧记录标记为isDeleted,并且不在storeall()查询中返回。删除操作之后会通过使用save()来进行持久化。此外,也可以使用destroyRecord来将删除和持久化一次完成。

var post = store.find('post', 1);post.deleteRecord();post.get('isDeleted'); // => truepost.save(); // => DELETE to /posts/1 // ORvar post = store.find('post', 2);post.destroyRecord(); // => DELETE to /posts/2

将记录推送进仓库

上节讲到仓库的概念,仓库类似于数据的缓冲池,当应用程序向仓库中查找不存在数据时,仓库则向数据源发起数据请求,如果存在的话,直接返回仓库中的数据。

推送记录

为了将记录推入仓库,需要调用仓库的push()方法。

var attr = DS.attr; App.Album = DS.Model.extend({  title: attr(),  artist: attr(),  songCount: attr()}); App.applicationRoute = Ember.Route.extend({  model: function() {    this.store.push('album', {      id: 1,      title: "Fewer Moving Parts",      artist: "David Bazan",      songCount: 10    });     this.store.push('album', {      id: 2,      title: "Calgary b/w I Can't Make You Love Me/Nick Of Time",      artist: "Bon Iver",      songCount: 2    });  }});

持久化记录

Ember Data中的记录都基于实例来进行持久化。调用DS.Model实例的save()会触发一个网络请求,来进行记录的持久化。

var post = store.createRecord('post', {  title: 'Rails is Omakase',  body: 'Lorem ipsum'});post.save(); // => POST to '/posts'

承诺

save()会返回一个承诺,这使得可以非常容易的来处理保存成功和失败的场景。下面是一个通用的模式:

var post = store.createRecord('post', {  title: 'Rails is Omakase',  body: 'Lorem ipsum'}); var 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

对于失败的网络请求,承诺也可以方便的来处理:

var post = store.createRecord('post', {  title: 'Rails is Omakase',  body: 'Lorem ipsum'}); var onSuccess = function(post) {  this.transitionToRoute('posts.show', post);}; var onFail = function(post) {  // deal with the failure here}; post.save().then(onSuccess, onFail); // => POST to '/posts'// => transitioning to posts.show route

更多关于承诺的内容请参看这里,下面是一个示例展示了如何在重试失败的持久化操作:

function retry(callback, nTimes) {  // if the promise fails  return callback().fail(function(reason) {    // if we haven't hit the retry limit    if (nTimes-- > 0) {      // retry again with the result of calling the retry callback      // and the new retry limit      return retry(callback, nTimes);    }     // otherwise, if we hit the retry limit, rethrow the error    throw reason;  });} // try to save the post up to 5 timesretry(function() {  return post.save();}, 5);

查询记录

Ember Data仓库提供了一个非常简单的查询一类记录的接口,该接口就是store对象的find方法。在内部,store根据传入的参数使用findfindAllfindQuery完成查询。store.find()的第一个参数是记录的类型,第二个可选参数确定查询是获取所有记录,还是一条记录,还是特定的记录。

查询一个类型的所有记录

var posts = this.store.find('post');

如果希望获取已经加载到仓库中的记录的列表,而不希望通过一个网络请求去获取,可以使用all方法:

var posts = this.store.all('post'); // => no network request

find会返回一个将使用DS.RecordArray来履行的DS.PromiseArray,而all直接返回DS.RecordArray

需要重点注意的一点是DS.RecordArray不是一个javascript数组。它是一个实现了Ember.Enumerable的对象。这一点非常重要,因为例如希望通过索引获取记录,那么[]将无法工作,需要使用objectAt(index)来获取。

查询一个记录

如果调用store.find()方法时,第二个参数是一个数字或者字符串,Ember Data将尝试获取对应ID的记录。find()方法将返回一个用请求的记录来履行的承诺。

var aSinglePost = this.store.find('post', 1); // => GET /posts/1

查询记录

如果传递给find方法的第二个参数是一个对象,Ember Data会发送一个使用该对象来序列化出来的查询参数的GET请求。这是方法返回与不加第二个参数时候一样的DS.PromiseArray

例如,可以查询名为Peterperson模型的所有记录:

var peters = this.store.find('person', { name: "Peter" }); // => GET to /persons?name='Peter'

与路由的模型钩子集成

如同在指定路由的模型一节中讨论的一样,路由是负责告诉模板将渲染哪个模型。

Ember.Routemodel钩子支持立即可用的异步值。如果model钩子返回一个承诺,路由将等待承诺履行条件满足时才渲染模板。

这使得使用Ember Data的异步数据来编写应用变得容易。只需要通过model钩子返回请求的记录,交个Ember来处理是否需要一个网络请求。

App.Router.map(function() {  this.resource('posts');  this.resource('post', { path: ':post_id' });}); App.PostsRoute = Ember.Route.extend({  model: function() {    return this.store.find('post');  }}); App.PostRoute = Ember.Route.extend({  model: function(params) {    return this.store.find('post', params.post_id);  }}
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表