Ember.js 概念详解--模型

来源:互联网 发布:财汇金融数据库 编辑:程序博客网 时间:2024/05/18 00:10

此文中讲到的ember-data已经是老版本,不推荐大家使用了。

由于是从word考过来的,格式不是太好,大家可以直接去下载我的完整word。里面除了翻译还有原创内容。

http://download.csdn.net/detail/kevinwon1985/5230326


在大多数Ember应用中,模型都通过Ember Data处理。Ember Data是一个专门用于框架的库。它设计目的是方便的从个服务端获取数据,在浏览器中改变数据,并将改变保存回服务器。

它提供了很多能在服务端找到的管理ORM的工具,例如活动记录。但是它是专门为了浏览器环境中的javascript设计的。

在遵循某些惯例的情况下,不需要很多配置,Ember Data通过RESTful JSON API能加载数据,保存数据和关系。

3.3.1 定义Store

每个使用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 定义模型

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连接器中,支持的属性类型有stringnumberboolean,和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 查找记录

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对象的具体内容框架并不知道,需要由服务器来解析,并且返回一个模型实例集合集。

3.3.4 修改属性

只有在一条记录已经加载,你才能修改它的属性。就像修改一个Ember对象一样。

var tyrion = App.Person.find(1);

// ...after the record has loaded

tyrion.set('firstName',"Yollo");

所有框架的便捷修改属性的方法都可以用。例如Ember.ObjectincrementProperty

person.incrementProperty('age');

你可以通过检测记录的isDirty属性来判断这条记录的改动是否已经保存。

person.get('isDirty');

//=> false

 

person.set('isAdmin', true);

 

person.get('isDirty');

//=> true

确保记录已经加载,再去修改它的属性。如果在loading状态修改,Ember Data会抛异常。

3.3.5 模型生命周期

DS.Model的实例都有一组布尔值的属性来判断记录的当前状态。

•       isLoaded 连接器已经从后台取回了记录。

•       isDirty 在本地修改了记录但是还没通过连接器保存。这也包括新建和删除的记录。

•       isSaving 连接器已经向后台发送了保存的请求,但是还没确认保存成功。

•       isDeleted 记录被标记为删除。当isDeleted为true,isDirty也为true,该记录只是在本地被删除,服务端还没删除。当isSaving为true,删除请求在发送中。当isDirtyisSaving都是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

服务端由于某些不正当的理由而没有保存。

3.3.6 REST

连接器

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方法进行映射。

App.Person = DS.Model.extend({

  lastName: DS.attr('string')

});

'App.Person', {

  lastName: {key:'lastNameOfPerson' }

});

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]

  }

}

postcomments可以通过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

如果position被修改了,与服务器同步时,传递的参数会通过serialize函数再次转变成一个数组。
原创粉丝点击