文章來源: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哈哈~