文章来源:http://xcoding.tech/tags/Emberjs
欢迎访问源网站Ember Teach,Ember Teach致力于为您提供最权威、最前沿的Ember技术教程。。css
adapter与serializer相对来讲是比较高级的内容。可是也是很是经常使用的一个组件,最经常使用到这两个组件的就是Ember Data。Ember Data已经实现了这两个组件,而且提供了很是丰富的API,若是要自定义适配器和序列化器通常都是扩展或者是重写JSONAPIAdapter,这也是官方建议的方式,JSONAPIAdapter是遵循json api规范的适配器,主要表如今数据的交换格式必须遵循JSONAPI规范。固然若是你想彻底从新实现一套自定义的适配器和序列化器也是能够的,官方建议是基于Adapter作扩展。因为能力有限本示例不会彻底自定义适配器和序列化器,示例仍然是使用官方推荐方式,重写或者扩展JSONAPIAdapter以实现自定适配器和序列化器。html
那么咱们为什么须要自定义适配器呢?很明显的一个缘由就是Ember.js并无提供数据存储方案,Ember目前更多的只是一个前端的MVC框架,数据的存储服务仍是要开发者提供,目前比较流行的作法是使用谷歌的Firebase,Firebase是一个实时更新数据的后端服务,国内也有相似的跟随者(野狗)可是目前野狗尚未Ember的适配器,相反Firebase提供了完备的Ember适配器,使用起来很是方便,并且还提供了免费的服务。能够说除了网速比较慢以外是很是适合作Ember的数据服务的。可是比较遗憾的是咱们在天朝内,你懂滴,谷歌的东西在咱们这个国度要使用仍是不怎么顺畅的。那么既然不能使用现有的轮子那么咱们只能使用本身的后端数据服务了,此时就必需要自定义适配器了(咱们能直接使用Firebase的缘由就是Firebase提供完美的适配器,不须要开发者去开发,直接使用便可)。前端
Ember所推崇是“约定因为配置”,全部Ember默认了不少规则,天然适配器和序列化器也不例外,因此本例子程序会包括两个方面的东西,一个是Ember项目自己;另外一个是为Ember项目提供服务器的后端数据库以及数据库处理程序。node
上述软件的安装与配置请自行根据各自官网文档介绍安装配置,若是你想使用其余数据库也是能够的,可是处理起来可能没有MongoDB那么方便,若是你看过jsonapi规范就知道,jsonapi相对于普通的json数据仍是有挺大差异的,若是是使用其余数据库(好比MySQL)处理起来可能稍微麻烦一些,另一个很重要的缘由就是json API插件大部分都是node版的,说了这么多其实就是想把服务器返回的数据格式格式化为jsonapi规范,不然其余格式的数据JSONAPIAdapter适配器是没法识别的,会报错。git
再啰嗦几句:一个APP之因此能与后端服务良好交互,其余交互的数据格式都是比较固定的,Ember也不例外,由于Ember Data所接受的数据格式是jsonapi,因此咱们的后端服务返回的数据格式必须符合jsonapi规范,固然若是你不是使用Ember Data,你使用的是其余的数据持久化库也是能够的,那么相对的你的的后端数据服务返回的数据格式就要跟你的持久化库相匹配就能够了,若是你想使用其余的持久化库你能够参考jsonapi client libraries,上面提供了各个语言的持久化库。github
言归正传,下面结合一个小例子讲解如何去自定义适配器去链接到本身的数据库,并把数据持久化到数据库中。mongodb
使用Ember CLI建立一个普通的Ember项目,命令以下:shell
ember new ember-adapter-serializer cd ember-adapter-serializer ember s
启动项目后预览http://localhost:4200,能够看到Ember常规的欢迎信息,说明项目建立成功。数据库
仍然是使用Ember CLI命令建立演示示例所需的路由、模型、模板文件,本示例会构建一个简单的博客项目,目的主要是为了使项目尽可能包含模型的一对1、一对多、多对多关系,这些关系是一个适配器比较关键的东西(简单理解,其实适配器就像MVC项目中的DAO层,专门作数据处理的)。json
建立命令以下:
ember g route users ember g model user ember g route users/list ember g route users/new ember g route users/edit ember g route posts ember g model post ember g route tags ember g model tag ember g route comments ember g model comment
后续使用到其余的文件再建立。
模型的处理主要是设置模型的属性以及模型之间的关系,在本例子中定义了3个模型:user
、post
、tag
、comment
。他们的关系以下:
各个模型代码以下:
// app/models/user.js import Model from 'ember-data/model'; import attr from 'ember-data/attr'; import { belongsTo,hasMany } from 'ember-data/relationships'; export default Model.extend({ name: attr('string'), password: attr('string'), birth: attr('string'), addr: attr('string'), //一对多关系,一的一方设置hasMay,多的一方设置belongsTo comments: hasMany('comment'), posts: hasMany('post') });
// app/models/post.js import Model from 'ember-data/model'; import attr from 'ember-data/attr'; import { belongsTo,hasMany } from 'ember-data/relationships'; export default Model.extend({ title: attr('string'), publicDate: attr('date'), content: attr('string'), user: belongsTo('user'), //一对多关系,多的一方使用belongsTo comments: hasMany('comment'), tags: hasMany('tag') });
// app/models/comment.js import Model from 'ember-data/model'; import attr from 'ember-data/attr'; import { belongsTo,hasMany } from 'ember-data/relationships'; export default Model.extend({ title: attr('string'), publicDate: attr('date'), content: attr('string'), user: belongsTo('user'), //一对多关系,多的一方使用belongsTo post: belongsTo('post') });
// app/models/tag.js import Model from 'ember-data/model'; import attr from 'ember-data/attr'; import { belongsTo,hasMany } from 'ember-data/relationships'; export default Model.extend({ title: attr('string'), posts: hasMany('post') });
建立完模型以后,先从user开始,简单作一个列表显示全部user,并在在列表页面能够新增、修改、删除user,实现最多见的CRUD操做。在实现的过程当中插入adapter和serializer的内容。
用户列表页面也是很简单的,就一个表格,其中引入了bootstrap样式。有关怎么引入请本身网上找答案吧。
{{! app/templates/users/list.hbs 用户列表}} <div class="row"> <div class="col-md-1 col-sx-11 col-md-offset-11 col-sx-offset-11"> {{#link-to 'users.new' class="btn btn-success"}}新增{{/link-to}} </div> </div> <div class="row"> <table class="table table-striped table-hover"> <thead> <tr> <th> 用户名 </th> <th> 生日 </th> <th> 地址 </th> <th> 操做 </th> </tr> </thead> <tbody> {{#each model as |user|}} <tr> <td> {{user.name}} </td> <td> {{user.birth}} </td> <td> {{user.addr}} </td> <td> {{#link-to 'users.edit' user.id}}修改{{/link-to}} | <a>删除</a> </td> </tr> {{/each}} </tbody> </table> </div>
import Ember from 'ember'; export default Ember.Route.extend({ model() { return this.store.findAll('user'); } });
列表的路由也很简单,直接获取全部的user记录,并返回到模板中,在模板中便利出每一个记录。
待项目重启完成,直接预览http://localhost:4200/users/list,能够看到页面上什么也没有显示,打开浏览器控制台能够看到以下错误:
很显然咱们的项目中确实没有提供请求http://localhost:4200/users
的后端服务,并且项目也没有连接其余任何数据服务(好比Firebase),那么如何让Ember项目连接到我本身的数据库上呢?
在此,先引入数据服务程序,前面介绍过,本例子使用Mongodb。服务端程序请看adapter-serializer-server,对于服务端的内容我就部过多介绍,你只须要知道这个服务程序能够接受、返回的数据格式是jsonapi就好了。而后启动后端服务程序。
那么如何让Ember项目连接到个人后端服务呢??很简单,只须要重写适配器的一个属性便可。下面使用Ember CLI名称建立一个适配器。
ember g adapter application
适配器建立完毕以后,咱们直接在适配器中接入本身的后端服务。代码以下:
// app/adapters/application.js import JSONAPIAdapter from 'ember-data/adapters/json-api'; export default JSONAPIAdapter.extend({ host: 'http://localhost:3000' });
http://localhost:3000
是adapter-serializer-server启动后提供服务的url。项目启动完毕后能够看到浏览器控制台的错误消失了!而且在“NetWork”标签下能够看到有一个请求http://localhost:3000/users
点击这个请求,查看请求的“Response”能够看到返回的数据,好比下面的数据格式:
{ "links": { "self": "http://localhost:3000/users" }, "data": [ { "id": "5753d7090280777c2381a0dd", "type": "users", "attributes": { "name": "日期修改333", "password": "11", "addr": "地址地址地址地址", "birth": "2016-06-04T00:00:00.000Z" }, "links": { "self": "http://127.0.0.1:3000/users/5753d7090280777c2381a0dd" }, "relationships": { "comments": { "data": [], "links": { "self": "http://127.0.0.1:3000/users/5753d7090280777c2381a0dd/relationships/comments" } }, "posts": { "data": [], "links": { "self": "http://127.0.0.1:3000/users/5753d7090280777c2381a0dd/relationships/posts" } } } }, { "id": "5753d74a840db09d2352608a", "type": "users", "attributes": { "name": "解决了跨域问题", "password": "123132", "addr": "范文芳啊的份水电费 测试服我飞" }, "links": { "self": "http://127.0.0.1:3000/users/5753d74a840db09d2352608a" }, "relationships": { "comments": { "data": [], "links": { "self": "http://127.0.0.1:3000/users/5753d74a840db09d2352608a/relationships/comments" } }, "posts": { "data": [], "links": { "self": "http://127.0.0.1:3000/users/5753d74a840db09d2352608a/relationships/posts" } } } }, { "id": "57545597840db09d2352608b", "type": "users", "attributes": { "name": "测试日期控件", "password": "123123", "addr": "适配器和序列化器示例" }, "links": { "self": "http://127.0.0.1:3000/users/57545597840db09d2352608b" }, "relationships": { "comments": { "data": [], "links": { "self": "http://127.0.0.1:3000/users/57545597840db09d2352608b/relationships/comments" } }, "posts": { "data": [], "links": { "self": "http://127.0.0.1:3000/users/57545597840db09d2352608b/relationships/posts" } } } } ] }
因为我项目已经有了3条数据了,因此跟你的可能有些许不一样,可是数据的格式是一致的。这个数据的格式是遵循jsonapi规范的。
上述代码所演示的是自定义适配器最最经常使用的一个功能,若是你后端返回的数据是彻底遵循jsonapi规范的几乎不须要再作其余任何修改了。这样就已经完成自定义适配器的工做了!可是既然在此特地介绍适配器和序列化器固然不会只是介绍这一个属性host
就完了,后面将陆陆续续介绍其余的属性,以及如何使用serializer。
继续完成user模块。
{{! app/templates/users/new.hbs 新增user}} <form> {{user-form model=model}} <button type="submit" class="btn btn-success" {{action 'saveUser' model}}>保存</button> </form>
直接在model
回调中返回一个空的实例对象。方便保存。
// app/routes/users/new.js import Ember from 'ember'; export default Ember.Route.extend({ model() { return this.store.createRecord('user'); }, actions: { saveUser(user) { user.save().then(() => { this.transitionTo('users.list');// 保存成功转到列表页面 }); } } });
{{! app/templates/users/edit.hbs 修改user}} <form> {{user-form model=model}} <button type="submit" class="btn btn-success" {{action 'updateUser' model}}>保存</button> {{#link-to 'users.list' class="btn btn-default"}}取消{{/link-to}} </form>
在修改的方法中先调用findRecord
方法查询出被修改的数据,而后更新修改的属性,再调用save
方法保存修改的内容。
// app/routes/users/edit.js import Ember from 'ember'; export default Ember.Route.extend({ // 根据ID查询 model(params) { return this.store.findRecord('user', params.user_id); }, actions: { updateUser(user) { this.store.findRecord('user', user.get('id')).then((u) => { u.set('name', user.get('name')); u.set('addr', user.get('addr')); u.set('birth', user.get('birth')); u.set('addr', user.get('addr')); u.save(); //保存修改的属性值 }); this.transitionTo('users.list'); //转到列表页面 } } });
因为新增、修改user模板都用到供一个表单,提取到一个组件中。
ember g component user-form
文件代码就不贴出来了,有须要请点击查看github代码。而后在组件类中初始化了一个日期控件bootstrap-datepicker,插件直接在app/index.html
中引入了,下面是组件类代码:
// app/components/user-form.js import Ember from 'ember'; export default Ember.Component.extend({ didInsertElement() { this._super(...arguments); //记得调用父类的构造方法 //初始化日期控件 Ember.$(".datepicker").datepicker({format:'yyyy-mm-dd', autoclose: true}); } });
user列表、新增user、修改user界面效果以下截图:
到此实现了相似使用Firebase的数据存储功能,能够正确保存数据到本身的数据库中。能够确定的是数据已经正确保存到个人MongoDB中,我就再也不截图了!对象的CRUD功能已经实现,后续我就再也不介绍post
、comment
、tag
的CRUD了,后续着重介绍适配器、序列化器的其余属性以及模型之间的关联关系(好比一对多、多对多)。
若是你认真看前面的第一个截图你会发现列表上显示的时间格式不友好,不是咱们所习惯看的时间格式,那么如何处理呢?格式化时间的方式有不少,能够自定义Ember helper格式化时间,也能够定义模型user
的属性birth
为date
类型,在此我特地定义为了string
是为了演示serializer的使用。咱们能够在自定义的serializer中格式化返回的数据。下面首先建立serializer。
ember g serializer application
在序列化器中调用响应请求的方法normalizeResponse
格式化返回的数据。代码以下:
// app/serializers/application.js import JSONAPISerializer from 'ember-data/serializers/json-api'; export default JSONAPISerializer.extend({ // 此方法响应请求的时候执行 normalizeResponse(store, primaryModelClass, payload, id, requestType) { // 格式化birth的时间格式 //默认显示的时间格式为 2016-06-09T00:00:00.000Z ,简单处理直接截取前面的10位 // 只是为了演示方法normalizeResponse的使用,实际项目中不推荐这样的用法,由于须要遍历每一个数据,效率很差 payload.data.forEach(function(item, index) { var oldDate = item.attributes.birth; if (oldDate) { oldDate = oldDate.substring(0, 10); } item.attributes.birth = oldDate; oldDate = null; }); return this._super(...arguments); } // 此方法发送请求的时候回执行 // serialize(snapshot, options) { // // } });
可是实际使用过程当中不推荐使用这种方式格式化数据,除非是不得已,由于须要遍历每一个记录去修改属性的值,若是数据量大影响效率,最好的方式是自定义helper
在模板上格式化。把格式化的操做放到显示数据的时候。
上述就是serializer的一个简单实用示例。
adapter和serializer内容比较多,分为2篇介绍,下一篇我回在本篇的基础上逐个介绍adapter和serializer的经常使用属性、方法的使用。
项目源码:https://github.com/ubuntuvim/ember-adapter-serializer
后台源码:https://github.com/ubuntuvim/adapter-serializer-server
有疑问欢迎在下方评论区给我留言,我会尽快为你解答,若是你以为本文能给你帮助,或者以为博主写做辛苦也欢迎点击右上角“为博主充电”给我打赏,你的确定对我来讲是最大的动力。O(∩_∩)O哈哈~