摘要:目前打算本項目使用種數(shù)據(jù)交互方式一種是,一種是。要理解后端服務(wù)的關(guān)系我們從他們各自的概念入手。創(chuàng)建服務(wù)端如何在項目中創(chuàng)建服務(wù)端程序呢提供了創(chuàng)建的命令。
文章來源:Ember Teach
本項目講解如何使用adapter、EmberData以及怎么連接到本地數(shù)據(jù)庫。
項目簡介 主要內(nèi)容適配器使用
如何持久化數(shù)據(jù)到本地數(shù)據(jù)庫
簡單的后端服務(wù)
最近經(jīng)常有初學(xué)的開發(fā)者請教有關(guān)Adapter或者Ember Data的問題。官方教程中講到這兩個內(nèi)容的是Model這一章節(jié)。本文中介紹到的內(nèi)容大部分是由這一章來的,如果有不妥請看原文或者給我留言。
注意:本文是基于v2.6.0講解。
軟件需求MySQL
nodejs,express
body-parser
mysql-node
Ember項目常規(guī)運行軟件Git
Node.js (with NPM)
Bower
Ember CLI
PhantomJS
用到的軟件、插件都是有關(guān)后端服務(wù)的,mysql-node用于連接、操作MySQL數(shù)據(jù)庫。后端服務(wù)是用node寫的所以也用node項目的插件連接、操作數(shù)據(jù)庫了,有關(guān)如何使用node操作MySQL的信息請看這篇文章[nodejs連接MySQL,做簡單的CRUD
](http://blog.ddlisting.com/201...。如果你的后端是其他語言寫只需要保證你后端返回的數(shù)據(jù)格式或者我的后端返回的數(shù)據(jù)格式一致就行了。目前打算本項目使用2種數(shù)據(jù)交互方式:一種是jsonapi,一種是restapi。
jsonapi
rest api
項目搭建項目的搭建就不再費口舌了,Ember Teach已經(jīng)有很多博文介紹過了。
運行項目如果你想運行本項目請按照下面的步驟操作:
安裝下載代碼到本地 git clone https://github.com/ubuntuvim/emberData-adapter-database
進(jìn)入項目目錄 cd emberData-adapter-database
安裝npm依賴包 npm install
安裝bower依賴包 bower install
運行在項目目錄下執(zhí)行命令 ember server 運行項目。
待項目啟動完畢,在瀏覽器打開http://localhost:4200。
發(fā)布到服務(wù)器執(zhí)行命令編譯、打包項目 ember build --environment production
命令執(zhí)行完畢會在dist目錄下得到項目打包后的文件。
把打包后的dist目錄下的所有文件復(fù)制到服務(wù)器應(yīng)用目錄下運行即可(比如tomcat服務(wù)器則放到webapps目錄下)。
項目結(jié)構(gòu)簡單起見我就做一個頁面就行了,我希望做出的效果是使用自定義的適配器獲取到本地MySQL數(shù)據(jù)庫的數(shù)據(jù)并分頁展示。
創(chuàng)建文件使用ember-cli命令創(chuàng)建文件。
ember g route users ember g model user username:string email:string ember g adapter application
目前暫時只用到這幾個文件,后續(xù)可能還有其他的用到在創(chuàng)建。
ember g model user username:string email:string的作用是創(chuàng)建模型的同時創(chuàng)建2個屬性,并且屬性都指定為string類型。
說了一大堆廢話下面開始正題。要理解adapter、ember data、后端服務(wù)的關(guān)系我們從他們各自的概念入手。首先我們先理清楚他們之間的關(guān)系然后在動手實踐。理論總是繁瑣的但是也是最重要的。
========================= 華麗的分割線 =========================
體系結(jié)構(gòu)概述注:圖片來自官方文檔
注意觀察上圖的結(jié)構(gòu)。
APP(一般是從route、controller或者component發(fā)請求)請求數(shù)據(jù)。
請求并沒有直接發(fā)送到后端服務(wù)而是先在store(ember data其實就是一個store)緩存中查找,ember之所以能實現(xiàn)動態(tài)更新模板數(shù)據(jù)也是因為有了store。
如果請求的數(shù)據(jù)存在在store中,則直接返回到route、controller或者component;如果在store中沒有發(fā)現(xiàn)請求的數(shù)據(jù),所以請求的數(shù)據(jù)是首次,數(shù)據(jù)還未緩存到store中,則請求繼續(xù)往下到了apdater層。
在adapter中,adapter會根據(jù)請求的調(diào)用方法構(gòu)建出對應(yīng)的URL。比如在route、controller或者component中執(zhí)行方法findRecord("user", 1),此方法作用是查詢id為1的user數(shù)據(jù)。適配器構(gòu)建出來的URL為: http://domain/user/1,然后發(fā)請求到后端。
適配器會對比后端接受的數(shù)據(jù)格式與ember data發(fā)送的數(shù)據(jù)格式,如果不一致需要在適配器的``方法中格式化發(fā)送的數(shù)據(jù)格式。請求經(jīng)過適配器構(gòu)建得到URL后發(fā)送到后端服務(wù),后端服務(wù)根據(jù)URL請求查詢數(shù)據(jù)庫然后格式化數(shù)據(jù)格式返回到適配器。
適配器根據(jù)得到的數(shù)據(jù)和ember data所接受的數(shù)據(jù)格式匹配,如果格式不一致需要在適配器的``方法中格式化后端返回的數(shù)據(jù)。
經(jīng)過適配器之后數(shù)據(jù)轉(zhuǎn)到ember data(store)中,首先緩存到store中,然后返回到調(diào)用處(route、controller、component)
數(shù)據(jù)請求完畢
注意:findRecord("user", 1)方法執(zhí)行過程,請求的findRecord("user", 1)方法會在Ember Data內(nèi)部解析為find方法,find方法會首先在store緩存中查數(shù)據(jù),如果沒有則會流轉(zhuǎn)到adapter中組裝URL并格式化請求數(shù)據(jù),然后發(fā)送到后端服務(wù)。
從圖中看到從適配器返回的數(shù)據(jù)是promise所以調(diào)用findRecord方法獲取數(shù)據(jù)的時候需要then()。同時可見這是個移步請求,只有promises執(zhí)行成功才能得到數(shù)據(jù)。也就是說如果考慮周全的話還需要在findRecord的時候處理promises執(zhí)行失敗的情況。
另外如果你想跳過store不需要這層緩存也是可以的。會可以這樣做:store.findRecord(type, id, { reload: true })使用reload屬性設(shè)置為true讓每次請求都跳過store直接發(fā)送請求到后端,對于實時性要求高的APP則需要這樣處理。
介紹完架構(gòu)之后將追個介紹其中的每個主要的功能特性。
需要說明的是:Models, records, adapters以及store都是Ember Data最核心的東西,他們是包含的關(guān)系,只要使用了Ember Data才能使用model、store功能。有些初學(xué)者老是問這幾個東西的關(guān)聯(lián),希望看到這里的同學(xué)不要在提這樣的問題了??!=^=
Ember Data是Ember.js非常重要的一塊,提供了幾乎所有操作數(shù)據(jù)的API,詳細(xì)請看EMBER-DATA MODULE。當(dāng)然,如果你不想使用Ember Data也是可以的,那么你的程序直接使用Ajax與后臺交互也是可以的,或者說你使用其他類似Ember Data的插件也行。Ember Data在MVC模式中屬于M層的東西,沒有這層也并不影響到整個APP!
補充一下下如果你不使用Ember Data,在這里提供一個簡單的方案供參考。
如果你想獲取后端數(shù)據(jù)并顯示數(shù)據(jù)到組件上(模板調(diào)用組件),你可以像下面的代碼這樣處理:
// app/components/list-of-drafts.js export default Ember.Component.extend({ willRender() { $.getJSON("/drafts").then(data => { this.set("drafts", data); }); } });
這里不同過Ember Data,自然也就沒有調(diào)用Ember Data提供的方法(比如,findAll、findRecord),而是直接發(fā)Ajax請求,得到數(shù)據(jù)到設(shè)置到對象drafts中,然后在模板上顯示數(shù)據(jù)。
這樣處理是沒問題的,但是當(dāng)數(shù)據(jù)改變的可能不能立即在模板上更新,因為這里無法使用store自然也就無法像計算屬性那樣當(dāng)數(shù)據(jù)有變就立即更新模板。另一個問題是當(dāng)你的請求很多的時候你需要寫很多這樣的方法,代碼復(fù)用性也比較差。
ModelsIn Ember Data, each model is represented by a subclass of Model that defines the attributes, relationships, and behavior of the data that you present to the user.
從使用上講,model其實就是與后端數(shù)據(jù)表對應(yīng)的實體類(借用java中的說法),通常我們的model類的定義是與后端數(shù)據(jù)表對應(yīng)的,最常見的就是model屬性的定義,建議屬性名和數(shù)據(jù)表字段名一致并且使用駝峰式命名規(guī)范。
model之間還可以定義單向或者雙向的一對一、一對多和多對多關(guān)系,這個與數(shù)據(jù)表之間的關(guān)系定義是相似的。比如下面的model:
簡單model定義//app/models/person.js import Model from "ember-data/model"; import attr from "ember-data/attr"; export default Model.extend({ firstName: attr("string"), birthday: attr("date") });
model類可以直接使用ember-cli命令創(chuàng)建:
ember g model person
上面代碼創(chuàng)建了一個簡單的model,并且包含了3個屬性,一個是string類型一個是date類型,那么第三個屬性是什么了??是id,Ember會默認(rèn)為每個model增加一個屬性id,開發(fā)者不需要手動去定義這個屬性,并且如果你是手動在model類中定義這個屬性會報錯的??!那么對應(yīng)后端的服務(wù)也應(yīng)該有一個person表,并且表里也有三個字段,它們是firstName、birthday以及id。
更多有關(guān)model之間關(guān)系的介紹不行本文的重點,請看第六章 模型的詳細(xì)介紹。
有了model之后程序要使用model類必須要實例化,實例化的model稱為records。
RecordsA record is an instance of a model that contains data loaded from a server. Your application can also create new records and save them back to the server. A record is uniquely identified by its model type and ID.
簡單講record就是一個包含數(shù)據(jù)的model實例。說白了就是一個JSON對象(雖然這樣的說法不是很正確,但是可以反映出這是一個什么樣的對象結(jié)構(gòu))。
比如下面的代碼:
this.get("store").findRecord("person", 1); // => { id: 1, name: "steve-buscemi" }
執(zhí)行完方法findRecord后返回的就是一個model實例也就是一個record。這個record包含了數(shù)據(jù){ id: 1, name: "steve-buscemi" }。
AdapterAn adapter is an object that translates requests from Ember (such as "find the user with an ID of 123") into requests to a server.
適配器,顧名思義!作用就是做適配工作的,保存轉(zhuǎn)換數(shù)據(jù)格式、定義交互的URL前綴、構(gòu)建URL等等。在前面體系結(jié)構(gòu)已經(jīng)詳細(xì)介紹過,不在贅述。
Caching緩存在Ember中是非常重要的,但是有一點需要注意的是不要把太多數(shù)據(jù)緩存到store中,數(shù)據(jù)量太大瀏覽器受不了!緩存的作用是非常明顯的,前面也介紹了他的作用,特別是在請求數(shù)據(jù)的時候,如果能在緩存中獲取的則立即返回到調(diào)用處,只有在緩存中查不到的數(shù)據(jù)才發(fā)請求到服務(wù)端,通常是第一次獲取的數(shù)據(jù)的時候緩存沒有則需要發(fā)請求到服務(wù)端。也正是有了緩存Ember才能快速把數(shù)據(jù)的變化響應(yīng)到模板上。
到此主要核心的概念介紹完畢了,不算多,但是認(rèn)真看下來還是很有益的??!
下面接著是如何實踐了……
創(chuàng)建數(shù)據(jù)庫本例子使用的是MySQL數(shù)據(jù)庫,有關(guān)數(shù)據(jù)庫的安裝以及使用不在本文講解范圍,請自行學(xué)習(xí)!
建表怎么建表我也不說了,下面直接貼建表的SQL。
DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(100) DEFAULT NULL, `email` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
創(chuàng)建一個名為user的數(shù)據(jù)表。
創(chuàng)建服務(wù)端如何在ember項目中創(chuàng)建服務(wù)端程序呢?ember提供了創(chuàng)建的命令。
ember g server
創(chuàng)建完畢之后再按照開始介紹的依賴插件。
npm install mysql-node npm install body-parser npm install supervisor
創(chuàng)建的是一個node服務(wù)端程序,運行的端口也是4200,不需要另外手動去啟動node服務(wù),只要ember項目運行了會自動運行起來的。
到此所有的原料都準(zhǔn)備好了,下面驗證一下項目是否還能正常運行。啟動項目,然后在瀏覽器打開http://localhost:4200。還能看到Welcome to Ember說明是成功的!
有了原料開始做菜吧!?。?/p> 編寫user模塊 更改URL方式
為了不使服務(wù)端和Ember請求URL沖突修改了URL的默認(rèn)方式,修改config/environment.js的第8行代碼為如下:
locationType: "hash",
auto改為hash。訪問Ember項目的URL則需要注意:http://localhost:4200/users改為http://localhost:4200/#/users。增加一個#號。
獲取數(shù)據(jù)、顯示數(shù)據(jù)首先簡單列出數(shù)據(jù)庫數(shù)據(jù)。
用戶列表
# | 用戶名 | 郵箱 |
---|---|---|
{{user.id}} | {{user.username}} | {{user.email}} |
// app/routes/users.js import Ember from "ember"; export default Ember.Route.extend({ model() { return this.store.findAll("user"); } });
目前項目還沒連接到任何數(shù)據(jù)庫,也沒有使用自定義的適配器,如果直接執(zhí)行http://localhost:4200/#/users可以在控制臺看到是會報錯的。那么下一步該如何處理呢??
加入適配器 使用RESTAdapter先從適配器下手!在前面已經(jīng)創(chuàng)建好了適配器,如果是2.0之后的項目默認(rèn)會創(chuàng)建JSONAPIAdapter這個適配器所接收、發(fā)送的數(shù)據(jù)格式都必須符合jsonapi規(guī)范,否則會報錯,無法正常完成數(shù)據(jù)的交互。不過為了簡便我們先不使用這個適配器,改用另一個簡單的適配器RESTAdapter,這個適配器不是需要遵循jsonapi規(guī)范,只要自己約定好前后端的數(shù)據(jù)格式即可。
// app/adapters/application.js // import JSONAPIAdapter from "ember-data/adapters/json-api"; import DS from "ember-data"; export default DS.RESTAdapter.extend({ });
手動修改好之后的適配器還不能起作用,這個適配器并沒有連接到任何的后端服務(wù),如果你想連接到你的服務(wù)上需要使用屬性host指定。
// app/adapters/application.js // import JSONAPIAdapter from "ember-data/adapters/json-api"; import DS from "ember-data"; export default DS.RESTAdapter.extend({ host: "http://localhost:4200" });
等待項目重啟完畢,仍然是訪問http://localhost:4200/#/user,在控制臺仍然看到前面的錯誤,截圖如下:
為何還是錯誤呢?如果能看到錯誤說明你的程序是正確,到目前為止還沒提供任何的后端服務(wù),雖然前面使用ember g server創(chuàng)建了node后端服務(wù),但是并沒有針對每個請求做處理。當(dāng)你訪問路由user在進(jìn)入回到model時候會發(fā)送請求獲取所有模型user數(shù)據(jù),請求首選轉(zhuǎn)到Ember Data(store),但是在store中并沒有,然后請求繼續(xù)轉(zhuǎn)到適配器RESTAdapter,適配器會構(gòu)建URL得到GET請求http://localhost:4200/users,至于是如何構(gòu)建URL的請看build url method。這個請求可以在報錯的信息中看到。但是為何會報錯呢?很正常,因為我的后端服務(wù)并沒響應(yīng)這個請求。下面針對這個請求做處理。
修改server/index.js。
/*jshint node:true*/ // To use it create some files under `mocks/` // e.g. `server/mocks/ember-hamsters.js` // // module.exports = function(app) { // app.get("/ember-hamsters", function(req, res) { // res.send("hello"); // }); // }; module.exports = function(app) { var globSync = require("glob").sync; var mocks = globSync("./mocks/**/*.js", { cwd: __dirname }).map(require); var proxies = globSync("./proxies/**/*.js", { cwd: __dirname }).map(require); // Log proxy requests var morgan = require("morgan"); app.use(morgan("dev")); // 對象轉(zhuǎn)json // const serialise = require("object-tojson") const bodyParser = require("body-parser"); mocks.forEach(function(route) { route(app); }); proxies.forEach(function(route) { route(app); }); app.use(bodyParser.urlencoded({ extended: true })); // 處理請求 http://localhost:4200/user app.get("/users", function(req, res) { // 返回三個對象 res.status(200).send({ users: [ { id: 1, username: "ubuntuvim", email: "123@qq.com" }, { id: 2, username: "ddlisting.com", email: "3333@qq.com" }, { id: 3, username: "www.ddlising.com", email: "1527254027@qq.com" } ] }); }); };
在服務(wù)端增加了一個node請求處理,攔截/users這個請求。對于express不是本文的重點,請自行學(xué)習(xí),網(wǎng)址expressjs.com。如果你使用的是其他語言的服務(wù)端程序,那么你只需要返回的json格式為:{"modelName":[{"id":1,"屬性名":"屬性值","屬性名2":"屬性值2"},{"id":2,"屬性名3":"屬性值3","屬性名4":"屬性4"}]},只需要格式正確適配器就能正確解析返回的數(shù)據(jù)。
另外再多介紹一個屬性namespace,這個屬性是用于定義URL前綴的,比如下面的適配器定義:
// app/adapters/application.js // import JSONAPIAdapter from "ember-data/adapters/json-api"; import DS from "ember-data"; export default DS.RESTAdapter.extend({ namespace: "api/v1", host: "http://localhost:4200" });
如果是這樣定義那么后端處理的URL也需要做相應(yīng)的處理,需要在攔截的請求上加前綴,比如下面的代碼。
// 處理請求 http://localhost:4200/api/v1/user app.get("/api/v1/users", function(req, res) { // 返回三個對象 res.status(200).send({ users: [ { id: 1, username: "ubuntuvim", email: "123@qq.com" }, { id: 2, username: "ddlisting.com", email: "3333@qq.com" }, { id: 3, username: "www.ddlising.com", email: "1527254027@qq.com" } ] }); });
之前面唯一不同的就是請求的URL不一樣了,原來是http://localhost:4200/users改為http://localhost:4200/api/v1/users。那么這樣做的好處是什么呢?當(dāng)你的后端的API更新的時候這個設(shè)置是非常有用的,只需要設(shè)置命名前綴就能適應(yīng)不用版本的API。
項目重啟之后,再次進(jìn)入到路由users可以看到返回的3條數(shù)據(jù)。如下截圖:
到此,我想你應(yīng)該知道個大概了吧?。「嘤嘘P(guān)適配器的介紹請看下面的2篇博文:
adapter與serializer使用示例一
adapter與serializer使用示例二
使用JSONAPIAdapter使用JSONAPIAdapter適配器和使用RESTAdapter適配器有何不同呢?我覺得最重要的一點是:數(shù)據(jù)規(guī)范。JSONAPIAdapter適配器要求交互的數(shù)據(jù)格式必須遵循jsonapi規(guī)范,否則是不能完成數(shù)據(jù)交互的。要求高了相應(yīng)的你的處理代碼也相應(yīng)的要復(fù)雜。下面我們改用JSONAPIAdapter處理。
// app/adapters/application.js import JSONAPIAdapter from "ember-data/adapters/json-api"; import DS from "ember-data"; // export default DS.RESTAdapter.extend({ export default JSONAPIAdapter.extend({ namespace: "api/v1", host: "http://localhost:4200" });
修改適配器為JSONAPIAdapter。如果你不修改后端的服務(wù)那么控制臺可以看到報錯信息。
從截圖當(dāng)中可以清楚地看到報錯出來的錯誤,must return a valid JSON API document必須是一個有效jsonapi文檔。要修復(fù)好這個錯誤也很簡單,只需要滾吧后端服務(wù)返回的數(shù)據(jù)格式改成jsonapi的就行了。請看下面的代碼:
// 處理請求 http://localhost:4200/user app.get("/api/v1/users", function(req, res) { // 返回三個對象 // res.status(200).send({ // users: [ // { // id: 1, // username: "ubuntuvim", // email: "123@qq.com" // }, // { // id: 2, // username: "ddlisting.com", // email: "3333@qq.com" // }, // { // id: 3, // username: "www.ddlising.com", // email: "1527254027@qq.com" // } // ] // }); // 構(gòu)建jsonapi對象 var input = { data: [ { id: "1", type: "user", //對應(yīng)前端程序中模型的名字 attributes: { // 模型中的屬性鍵值對 username: "ubuntuvim", property: true, email: "123@qq.com", property: true } }, { id: "2", type: "user", //對應(yīng)前端程序中模型的名字 attributes: { // 模型中的屬性鍵值對 username: "ddlisting.com", property: true, email: "3333@qq.com", property: true } }, { id: "3", type: "user", //對應(yīng)前端程序中模型的名字 attributes: { // 模型中的屬性鍵值對 username: "www.ddlising.com", property: true, email: "1527254027@qq.com", property: true } } ] }; res.status(200).send(JSON.stringify(input)); });
注:為了構(gòu)建jsonapi對象更加簡便另外在安裝一個插件: npm install jsonapi-parse。安裝完畢后手動關(guān)閉再重啟項目。然后再次進(jìn)入路由users可以看到與前面的結(jié)果一樣,正確了顯示后端返回的數(shù)據(jù)。
到此,我相信讀者應(yīng)該能明白這兩個適配器之間的差別了!需要注意的是Ember.js2.0版本之后JSONAPIAdapter作為默認(rèn)的適配器,也就是說平常如果你沒有自定義任何適配器那么Ember Data會默認(rèn)使用的是JSONAPIAdapter適配器。所以如果你沒有使用其他的適配器那么你的后端返回的數(shù)據(jù)格式必須是遵循jsonapi規(guī)范的。另外在路由users.js中使用到Ember Data提供的方法findAll("modelName"),我想從中你也應(yīng)該明白了Ember Data是何等重要了吧
看到這里不知道讀者是否已經(jīng)明白適配器和后端服務(wù)的關(guān)聯(lián)關(guān)系?如果有疑問請給我留言。
文中所說的后端就是我的node程序(放在server目錄下),前端就是我的Ember.js項目。
下面就是再結(jié)合數(shù)據(jù)庫。
加入數(shù)據(jù)庫其實到這步加不加數(shù)據(jù)庫已經(jīng)不那么重要了!重要把服務(wù)端返回的數(shù)據(jù)改成從數(shù)據(jù)庫讀取就完了。我就簡單講解了。
連接MySQL連接MySQL的工作交給前面已經(jīng)安裝好的node-mysql,如果還沒安裝請執(zhí)行命令npm install mysqljs/mysql進(jìn)行安裝。繼續(xù)修改后端服務(wù)代碼server/index.js。
module.exports = function(app) { // 與之前的內(nèi)容不變 // // 引入MySQL模塊 var mysql = require("mysql"); // 獲取連接對象 var conn = mysql.createConnection({ host: "localhost", user: "root", password: "", // 開啟debug,可以在啟動ember項目的終端看到更多詳細(xì)的信息 database: "test" }); // 處理請求 http://localhost:4200/user app.get("/api/v1/users", function(req, res) { var jsonArr = new Array(); // 打開數(shù)據(jù)庫連接 conn.connect(); //查詢數(shù)據(jù) conn.query("select * from user", function(err, rows, fields) { if (err) throw err; //遍歷返回的數(shù)據(jù)并設(shè)置到返回的json對象中 for (var i = 0; i < rows.length; i++) { jsonArr.push({ id: rows[i].id, username: rows[i].username, email: rows[i].email }); } // 返回前端 res.status(200).send({ users: jsonArr }); }); // 關(guān)閉數(shù)據(jù)庫連接 conn.end(); }); };
相比之前的代碼只是引入了mysql,增加連接對象聲明,然后在請求處理方法里查詢數(shù)據(jù),默認(rèn)在數(shù)據(jù)庫初始化了3條數(shù)據(jù),如下截圖,另外 為了簡單起見我仍然使用的是RESTAdapter適配器,這樣處理也相對簡單。 獲取連接對象的代碼應(yīng)該不用過多解釋了,就是填寫你本地連接數(shù)據(jù)庫的對應(yīng)配置信息就行了。
記得修改適配器為RESTAdapter。
重啟項目。進(jìn)入路由users可以看到數(shù)據(jù)庫的數(shù)據(jù)正確顯示出來了。
CRUD操作對于CRUD操作都舉一個例子,由于前面已經(jīng)介紹過findAll查詢就不在此介紹CRUD中的R了。下面就對另外三個做一個例子:
更多有關(guān)數(shù)據(jù)的操作請看Ember.js 入門指南——新建、更新、刪除記錄。
為了方便演示再增加幾個路由和模板。
ember g template users/index ember g route users/new ember g route users/edit
上述3個命令創(chuàng)建了三個users的子路由和子模板。
新增、更新由于項目使用的是Ember Data,增加數(shù)據(jù)也是很簡單的,直接調(diào)用createRecord()創(chuàng)建一個record之后再調(diào)用save()方法保存到服務(wù)器。
另外新增和更新的處理方式相似,就直接寫在一個方法內(nèi)。
// app/components/user-form.js // 新增,修改user import Ember from "ember"; export default Ember.Component.extend({ tipInfo: null, actions: { saveOrUpdate(id, user) { if (id) { //更新 let username = this.get("model.username"); let email = this.get("model.email"); if (username && email) { this.store.findRecord("user", id).then((u) => { u.set("username", username); u.set("email", email); u.save().then(() => { this.set("tipInfo", "更新成功"); // this.set("model.username", ""); // this.set("model.email", ""); }); }); } else { this.set("tipInfo", "請輸入username和email!"); } } else { //新增 let username = this.get("model.username"); let email = this.get("model.email"); if (username && email) { this.get("store").createRecord("user", { username: username, email: email }).save().then(() => { this.set("tipInfo", "保存成功"); this.set("model.username", ""); this.set("model.email", ""); }, (err) => { this.set("tipInfo", "保存失敗"+err); }); } else { this.set("tipInfo", "請輸入username和email!"); } } } } });
新增和修改處理是相似的,根據(jù)id是否為空判斷是否是新增還是更新。
hbs:user-form.hbs{{! 新增、修改都用到的表單,提出為公共部分}}{{title}}
{{#if tipInfo}}{{#link-to "users" class="btn btn-primary"}}返回{{/link-to}}
{{tipInfo}}{{/if}}
// app/routes/users/edit.js import Ember from "ember"; export default Ember.Route.extend({ // 根據(jù)id獲取某個記錄 model(params) { return this.store.findRecord("user", params.user_id); } });
點擊“編輯”的時候需要根據(jù)被點擊記錄的id查詢數(shù)據(jù)詳情,并返回到編輯頁面。
{{! 增加數(shù)據(jù)的表單}} {{user-form title="新增user" store=store model=model}}
{{! 修改數(shù)據(jù)的表單}} {{user-form title="修改user" store=store model=model}}
提取新增和修改這兩個模板的相同代碼為一個組件,兩個模板都調(diào)用組件。
后端處理代碼與前端對應(yīng)的要有相應(yīng)的后端處理服務(wù),增加2個路由監(jiān)聽,一個是監(jiān)聽post提交(新增),一個是put提交(更新)。
// 處理請求 POST http://localhost:4200/users app.post("/api/v1/users", function(req, res) { var username = req.body.user.username; console.log("req.body.user.username = " + username); var email = req.body.user.email; console.log("req.body.user.email = " + email); // 打開數(shù)據(jù)庫連接 pool.getConnection(function(err, conn) { var queryParams = { username: username, email: email }; var query = conn.query("insert into user SET ?", queryParams, function(err, result) { if (err) throw err; console.log("result = " + result); // 返回前端 if (result) { res.status(200).send({ users: { id: result.insertId, username: username, email: email } }); } else { //沒有數(shù)據(jù)返回一個空的 // 返回前端 res.status(200).send({ users: { id: "", username: "", email: "" } }); } }); console.log("sql: " + query.sql); conn.release(); //釋放連接,放回到連接池 }); }); // 處理請求 POST http://localhost:4200/users/id 根據(jù)id更新某個數(shù)據(jù) app.put("/api/v1/users/:id", function(req, res) { console.log("更新 POST /api/v1/users/:id"); console.log("req.params.id = " + req.params.id); console.log("req.body.user.username = " + req.body.user.username); var jsonArr = new Array(); // 打開數(shù)據(jù)庫連接 pool.getConnection(function(err, conn) { // 參數(shù)的次序要與SQL語句的參數(shù)次序一致 var queryParams = [ req.body.user.username, req.body.user.email, req.params.id ]; var query = conn.query("UPDATE user SET username = ?, email = ? where id = ?", queryParams, function(err, results, fields) { if (err) { console.log("更新出錯:"+err); throw err; } //遍歷返回的數(shù)據(jù)并設(shè)置到返回的json對象中,通常情況下只有一個數(shù)據(jù),直接取第一個數(shù)據(jù)返回 if (results && results.length > 0) { jsonArr.push({ id: results[0].id, username: results[0].username, email: results[0].email }); // 返回前端 res.status(200).send({ users: jsonArr }); } // else { //沒有數(shù)據(jù)返回一個空的 // // 返回前端 // res.status(200).send({ // users: { // id: "", // username: "", // email: "" // } // }); // } console.log("SQL: " + query.sql); }); conn.release(); //釋放連接,放回到連接池 }); });
為何新增對應(yīng)的是post方法,更新對應(yīng)的是put方法,請看the rest adapter的詳細(xì)介紹(主要是第一個表格的內(nèi)容)。
簡單測試點擊右上角的新增按鈕進(jìn)入新增界面。
進(jìn)入新增界面后輸入相應(yīng)信息(我就不做數(shù)據(jù)的格式校驗了,有需要自己校驗數(shù)據(jù)格式)。然后點擊“保存”,保存成功會有提示信息。
點擊右上角的“返回”回到主列表頁面,查看新增的數(shù)據(jù)是否保存成功。
可以看到剛剛新增的數(shù)據(jù)已經(jīng)顯示在列表上,為了進(jìn)一步驗證數(shù)據(jù)已經(jīng)保存成功,直接查看數(shù)據(jù)庫。
可以看到數(shù)據(jù)庫也已經(jīng)成功保存了剛剛新增的數(shù)據(jù)。
修改的測試方式我就不啰嗦了,點擊列表上的修改按鈕進(jìn)入修改頁面,修改后保存既可以,請自行測試。
刪除刪除處理相比新增更加簡單,直接發(fā)送一個delete請求即可。
Ember前端處理// app/routes/user.js import Ember from "ember"; export default Ember.Route.extend({ model() { return this.store.findAll("user"); }, actions: { // 刪除記錄 del(id) { console.log("刪除記錄:" + id); this.get("store").findRecord("user", id).then((u) => { u.destroyRecord(); // => DELETE to /users/2 }); } } });
用戶列表
{{#link-to "users.new" class="btn btn-primary"}}新增{{/link-to}}
# | 用戶名 | 郵箱 | 操作 |
---|---|---|---|
{{user.id}} | {{user.username}} | {{user.email}} | {{#link-to "users.edit" user.id}}修改{{/link-to}} | 刪除 |
這段代碼的與前面的代碼基本一致,就是增加了刪除。
后端處理在后端增加一個監(jiān)聽刪除的路由。
// 處理請求 DELETE http://localhost:4200/users/id 刪除記錄 app.delete("/api/v1/users/:id", function(req, res) { var jsonArr = new Array(); var id = req.params.id; console.log("刪除 req.params.id = " + id); // 打開數(shù)據(jù)庫連接 pool.getConnection(function(err, conn) { var queryParams = [ id ]; var query = conn.query("delete from user where id = ?", queryParams, function(err, result) { if (err) throw err; // 返回前端 res.status(200).send({}); }); console.log("sql: " + query.sql); conn.release(); //釋放連接,放回到連接池 }); });測試刪除
測試刪除很簡單,直接在列表上點擊“刪除”按鈕即可刪除一條記錄。界面和數(shù)據(jù)庫的截圖我就不貼出來了,自己動手測試就知道了?。?/p>
數(shù)據(jù)可以正確刪除,但是,刪除之后控制臺會報如下錯誤:
找了官網(wǎng)文檔the rest adapter delete record按照官網(wǎng)的文檔處理仍然報錯!目前還沒找到好的處理方法,不知道是哪里出了問題,如果讀者知道請告訴我,謝謝。
到此CRUD操作也完成了,不足的就是在處理刪除的時候還是有點問題,目前還沒找到覺得辦法!但是總的來說對于CRUD的操作都是這么處理的,調(diào)用的方法也都是上述代碼所使用的方法。
未完待續(xù)……還差分頁沒完成。
總結(jié)文章寫到這里已經(jīng)把我所想的內(nèi)容介紹完畢了,不知道讀者是否看明白了。其中主要理解的知識點是:
Ember Data和adapter、record、model的關(guān)系
如何自定義適配器
如何根據(jù)Ember前端請求編寫后端處理
CRUD操作
分頁處理(目前還沒整合進(jìn)來)
明白了上述幾點,本文的目的也達(dá)到了!如何有疑問歡迎給我留言,也期待著讀者能給我解答刪除報錯的問題!
文章源碼如果有需要歡迎star或者fork學(xué)習(xí)。下面是源碼地址:
https://github.com/ubuntuvim/emberData-adapter-database,歡迎follow我,一起學(xué)習(xí)交流!我在全球最大的同性交友網(wǎng)站等你哦?。?/p> 參考網(wǎng)址
ember.js
ember-cli
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/91636.html
摘要:由于能力有限本示例不會完全自定義適配器和序列化器,示例仍然是使用官方推薦方式,重寫或者擴展以實現(xiàn)自定適配器和序列化器。在序列化器中調(diào)用響應(yīng)請求的方法格式化返回的數(shù)據(jù)。上述就是的一個簡單實用示例。 文章來源:http://xcoding.tech/tags/Emberjs歡迎訪問源網(wǎng)站Ember Teach,Ember Teach致力于為您提供最權(quán)威、最前沿的Ember技術(shù)教程。。 ad...
摘要:就沒必要動牛刀,創(chuàng)建一個數(shù)據(jù)庫了執(zhí)行完后,在目錄下創(chuàng)建一個程序,自動植入到當(dāng)前項目中,訪問的和與訪問域名端口一致。就沒必要動牛刀,創(chuàng)建一個數(shù)據(jù)庫了本篇博文將為你介紹如何使用實現(xiàn)權(quán)限控制,我會創(chuàng)建一個簡單的登錄示例加以說明。 文章來源:http://blog.ddlisting.com 官網(wǎng)對于登錄、用戶權(quán)限的介紹只有一段簡單的說明,并沒有詳細(xì)說明如何使用service實現(xiàn)權(quán)限控制。下面...
摘要:在文件夾內(nèi)創(chuàng)建,內(nèi)容如下創(chuàng)建,內(nèi)容如下使用安裝依賴在的頭部加入調(diào)用命令,同時在你的默認(rèn)瀏覽器中打開。最后,我們更新下,給每個報道添加鏈接修改完畢地后,可以在瀏覽器中直接看到結(jié)果。 編者注:我們發(fā)現(xiàn)了有趣的系列文章《30天學(xué)習(xí)30種新技術(shù)》,正在翻譯,一天一篇更新,年終禮包。下面是第19天的內(nèi)容。 到目前為止,我們這一系列文章涉及了Bower、AngularJS、GruntJS、P...
摘要:創(chuàng)建模型并設(shè)置關(guān)聯(lián)關(guān)聯(lián)關(guān)系設(shè)置模型關(guān)系一個對應(yīng)多個,一個對應(yīng)多個。手動在中增加關(guān)聯(lián)關(guān)系。并且是實現(xiàn)了數(shù)據(jù)表之間的關(guān)聯(lián)關(guān)系,比如一個對應(yīng)多個,如下圖。 文章來源:模型高級特性,引入模型關(guān)聯(lián)關(guān)系 接著前面五篇: 環(huán)境搭建以及使用Ember.js創(chuàng)建第一個靜態(tài)頁面 引入計算屬性、action、動態(tài)內(nèi)容 模型,保存數(shù)據(jù)到數(shù)據(jù)庫 發(fā)布項目,加入CRUD功能 從服務(wù)器獲取數(shù)據(jù),引入組件 前言 ...
摘要:文章來源模型,保存數(shù)據(jù)到數(shù)據(jù)庫環(huán)境搭建以及使用創(chuàng)建第一個靜態(tài)頁面引入計算屬性動態(tài)內(nèi)容繼續(xù)為讀者介紹如何使用構(gòu)建一個完整的復(fù)雜的項目。 文章來源:模型,保存數(shù)據(jù)到數(shù)據(jù)庫 環(huán)境搭建以及使用Ember.js創(chuàng)建第一個靜態(tài)頁面 引入計算屬性、action、動態(tài)內(nèi)容 繼續(xù)為讀者介紹如何使用Ember構(gòu)建一個完整的、復(fù)雜的項目。 第一個Ember.js模型 在前面兩篇中實現(xiàn)了如何獲取界面輸入的...
閱讀 2336·2021-11-22 13:52
閱讀 4122·2021-11-10 11:36
閱讀 1530·2021-09-24 09:47
閱讀 1144·2019-08-29 13:54
閱讀 3424·2019-08-29 13:46
閱讀 2014·2019-08-29 12:16
閱讀 2174·2019-08-26 13:26
閱讀 3530·2019-08-23 17:10