成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Ember.js如何與后端服務(wù)交互?adapter、store、ember data關(guān)系揭秘

huhud / 1784人閱讀

摘要:目前打算本項(xiàng)目使用種數(shù)據(jù)交互方式一種是,一種是。要理解后端服務(wù)的關(guān)系我們從他們各自的概念入手。創(chuàng)建服務(wù)端如何在項(xiàng)目中創(chuàng)建服務(wù)端程序呢提供了創(chuàng)建的命令。

文章來源:Ember Teach

本項(xiàng)目講解如何使用adapter、EmberData以及怎么連接到本地?cái)?shù)據(jù)庫。

項(xiàng)目簡(jiǎn)介 主要內(nèi)容

適配器使用

如何持久化數(shù)據(jù)到本地?cái)?shù)據(jù)庫

簡(jiǎn)單的后端服務(wù)

最近經(jīng)常有初學(xué)的開發(fā)者請(qǐng)教有關(guān)Adapter或者Ember Data的問題。官方教程中講到這兩個(gè)內(nèi)容的是Model這一章節(jié)。本文中介紹到的內(nèi)容大部分是由這一章來的,如果有不妥請(qǐng)看原文或者給我留言。

注意:本文是基于v2.6.0講解。

軟件需求

MySQL

nodejs,express

body-parser

mysql-node

Ember項(xiàng)目常規(guī)運(yùn)行軟件

Git

Node.js (with NPM)

Bower

Ember CLI

PhantomJS

用到的軟件、插件都是有關(guān)后端服務(wù)的,mysql-node用于連接、操作MySQL數(shù)據(jù)庫。后端服務(wù)是用node寫的所以也用node項(xiàng)目的插件連接、操作數(shù)據(jù)庫了,有關(guān)如何使用node操作MySQL的信息請(qǐng)看這篇文章[nodejs連接MySQL,做簡(jiǎn)單的CRUD
](http://blog.ddlisting.com/201...。如果你的后端是其他語言寫只需要保證你后端返回的數(shù)據(jù)格式或者我的后端返回的數(shù)據(jù)格式一致就行了。目前打算本項(xiàng)目使用2種數(shù)據(jù)交互方式:一種是jsonapi,一種是restapi。

jsonapi

rest api

項(xiàng)目搭建

項(xiàng)目的搭建就不再費(fèi)口舌了,Ember Teach已經(jīng)有很多博文介紹過了。

運(yùn)行項(xiàng)目

如果你想運(yùn)行本項(xiàng)目請(qǐng)按照下面的步驟操作:

安裝

下載代碼到本地 git clone https://github.com/ubuntuvim/emberData-adapter-database

進(jìn)入項(xiàng)目目錄 cd emberData-adapter-database

安裝npm依賴包 npm install

安裝bower依賴包 bower install

運(yùn)行

在項(xiàng)目目錄下執(zhí)行命令 ember server 運(yùn)行項(xiàng)目。

待項(xiàng)目啟動(dòng)完畢,在瀏覽器打開http://localhost:4200。

發(fā)布到服務(wù)器

執(zhí)行命令編譯、打包項(xiàng)目 ember build --environment production

命令執(zhí)行完畢會(huì)在dist目錄下得到項(xiàng)目打包后的文件。

把打包后的dist目錄下的所有文件復(fù)制到服務(wù)器應(yīng)用目錄下運(yùn)行即可(比如tomcat服務(wù)器則放到webapps目錄下)。

項(xiàng)目結(jié)構(gòu)

簡(jiǎn)單起見我就做一個(gè)頁面就行了,我希望做出的效果是使用自定義的適配器獲取到本地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

目前暫時(shí)只用到這幾個(gè)文件,后續(xù)可能還有其他的用到在創(chuàng)建。
ember g model user username:string email:string的作用是創(chuàng)建模型的同時(shí)創(chuàng)建2個(gè)屬性,并且屬性都指定為string類型。

說了一大堆廢話下面開始正題。要理解adapterember data、后端服務(wù)的關(guān)系我們從他們各自的概念入手。首先我們先理清楚他們之間的關(guān)系然后在動(dòng)手實(shí)踐。理論總是繁瑣的但是也是最重要的。

========================= 華麗的分割線 =========================

體系結(jié)構(gòu)概述

注:圖片來自官方文檔

注意觀察上圖的結(jié)構(gòu)。

APP(一般是從route、controller或者component發(fā)請(qǐng)求)請(qǐng)求數(shù)據(jù)。

請(qǐng)求并沒有直接發(fā)送到后端服務(wù)而是先在store(ember data其實(shí)就是一個(gè)store)緩存中查找,ember之所以能實(shí)現(xiàn)動(dòng)態(tài)更新模板數(shù)據(jù)也是因?yàn)橛辛?b>store。

如果請(qǐng)求的數(shù)據(jù)存在在store中,則直接返回到route、controller或者component;如果在store中沒有發(fā)現(xiàn)請(qǐng)求的數(shù)據(jù),所以請(qǐng)求的數(shù)據(jù)是首次,數(shù)據(jù)還未緩存到store中,則請(qǐng)求繼續(xù)往下到了apdater層。

adapter中,adapter會(huì)根據(jù)請(qǐng)求的調(diào)用方法構(gòu)建出對(duì)應(yīng)的URL。比如在route、controller或者component中執(zhí)行方法findRecord("user", 1),此方法作用是查詢id為1的user數(shù)據(jù)。適配器構(gòu)建出來的URL為: http://domain/user/1,然后發(fā)請(qǐng)求到后端。

適配器會(huì)對(duì)比后端接受的數(shù)據(jù)格式與ember data發(fā)送的數(shù)據(jù)格式,如果不一致需要在適配器的``方法中格式化發(fā)送的數(shù)據(jù)格式。請(qǐng)求經(jīng)過適配器構(gòu)建得到URL后發(fā)送到后端服務(wù),后端服務(wù)根據(jù)URL請(qǐng)求查詢數(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)用處(routecontroller、component

數(shù)據(jù)請(qǐng)求完畢

注意:findRecord("user", 1)方法執(zhí)行過程,請(qǐng)求的findRecord("user", 1)方法會(huì)在Ember Data內(nèi)部解析為find方法,find方法會(huì)首先在store緩存中查數(shù)據(jù),如果沒有則會(huì)流轉(zhuǎn)到adapter中組裝URL并格式化請(qǐng)求數(shù)據(jù),然后發(fā)送到后端服務(wù)。

從圖中看到從適配器返回的數(shù)據(jù)是promise所以調(diào)用findRecord方法獲取數(shù)據(jù)的時(shí)候需要then()。同時(shí)可見這是個(gè)移步請(qǐng)求,只有promises執(zhí)行成功才能得到數(shù)據(jù)。也就是說如果考慮周全的話還需要在findRecord的時(shí)候處理promises執(zhí)行失敗的情況。

另外如果你想跳過store不需要這層緩存也是可以的。會(huì)可以這樣做:store.findRecord(type, id, { reload: true })使用reload屬性設(shè)置為true讓每次請(qǐng)求都跳過store直接發(fā)送請(qǐng)求到后端,對(duì)于實(shí)時(shí)性要求高的APP則需要這樣處理。

介紹完架構(gòu)之后將追個(gè)介紹其中的每個(gè)主要的功能特性。
需要說明的是:Models, records, adapters以及store都是Ember Data最核心的東西,他們是包含的關(guān)系,只要使用了Ember Data才能使用model、store功能。有些初學(xué)者老是問這幾個(gè)東西的關(guān)聯(lián),希望看到這里的同學(xué)不要在提這樣的問題了!!=^=

Ember Data是Ember.js非常重要的一塊,提供了幾乎所有操作數(shù)據(jù)的API,詳細(xì)請(qǐng)看EMBER-DATA MODULE。當(dāng)然,如果你不想使用Ember Data也是可以的,那么你的程序直接使用Ajax與后臺(tái)交互也是可以的,或者說你使用其他類似Ember Data的插件也行。Ember Data在MVC模式中屬于M層的東西,沒有這層也并不影響到整個(gè)APP!

補(bǔ)充一下下

如果你不使用Ember Data,在這里提供一個(gè)簡(jiǎn)單的方案供參考。
如果你想獲取后端數(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請(qǐng)求,得到數(shù)據(jù)到設(shè)置到對(duì)象drafts中,然后在模板上顯示數(shù)據(jù)。


    {{#each drafts key="id" as |draft|}}
  • {{draft.title}}
  • {{/each}}

這樣處理是沒問題的,但是當(dāng)數(shù)據(jù)改變的可能不能立即在模板上更新,因?yàn)檫@里無法使用store自然也就無法像計(jì)算屬性那樣當(dāng)數(shù)據(jù)有變就立即更新模板。另一個(gè)問題是當(dāng)你的請(qǐng)求很多的時(shí)候你需要寫很多這樣的方法,代碼復(fù)用性也比較差。

Models

In 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í)就是與后端數(shù)據(jù)表對(duì)應(yīng)的實(shí)體類(借用java中的說法),通常我們的model類的定義是與后端數(shù)據(jù)表對(duì)應(yīng)的,最常見的就是model屬性的定義,建議屬性名和數(shù)據(jù)表字段名一致并且使用駝峰式命名規(guī)范。

model之間還可以定義單向或者雙向的一對(duì)一、一對(duì)多和多對(duì)多關(guān)系,這個(gè)與數(shù)據(jù)表之間的關(guān)系定義是相似的。比如下面的model:

簡(jiǎn)單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)建了一個(gè)簡(jiǎn)單的model,并且包含了3個(gè)屬性,一個(gè)是string類型一個(gè)是date類型,那么第三個(gè)屬性是什么了??是id,Ember會(huì)默認(rèn)為每個(gè)model增加一個(gè)屬性id,開發(fā)者不需要手動(dòng)去定義這個(gè)屬性,并且如果你是手動(dòng)在model類中定義這個(gè)屬性會(huì)報(bào)錯(cuò)的??!那么對(duì)應(yīng)后端的服務(wù)也應(yīng)該有一個(gè)person表,并且表里也有三個(gè)字段,它們是firstName、birthday以及id。

更多有關(guān)model之間關(guān)系的介紹不行本文的重點(diǎn),請(qǐng)看第六章 模型的詳細(xì)介紹。

有了model之后程序要使用model類必須要實(shí)例化,實(shí)例化的model稱為records。

Records

A 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.

簡(jiǎn)單講record就是一個(gè)包含數(shù)據(jù)的model實(shí)例。說白了就是一個(gè)JSON對(duì)象(雖然這樣的說法不是很正確,但是可以反映出這是一個(gè)什么樣的對(duì)象結(jié)構(gòu))。

比如下面的代碼:

this.get("store").findRecord("person", 1); // => { id: 1, name: "steve-buscemi" }

執(zhí)行完方法findRecord后返回的就是一個(gè)model實(shí)例也就是一個(gè)record。這個(gè)record包含了數(shù)據(jù){ id: 1, name: "steve-buscemi" }。

Adapter

An 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中是非常重要的,但是有一點(diǎn)需要注意的是不要把太多數(shù)據(jù)緩存到store中,數(shù)據(jù)量太大瀏覽器受不了!緩存的作用是非常明顯的,前面也介紹了他的作用,特別是在請(qǐng)求數(shù)據(jù)的時(shí)候,如果能在緩存中獲取的則立即返回到調(diào)用處,只有在緩存中查不到的數(shù)據(jù)才發(fā)請(qǐng)求到服務(wù)端,通常是第一次獲取的數(shù)據(jù)的時(shí)候緩存沒有則需要發(fā)請(qǐng)求到服務(wù)端。也正是有了緩存Ember才能快速把數(shù)據(jù)的變化響應(yīng)到模板上。

到此主要核心的概念介紹完畢了,不算多,但是認(rèn)真看下來還是很有益的!!

下面接著是如何實(shí)踐了……

創(chuàng)建數(shù)據(jù)庫

本例子使用的是MySQL數(shù)據(jù)庫,有關(guān)數(shù)據(jù)庫的安裝以及使用不在本文講解范圍,請(qǐng)自行學(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)建一個(gè)名為user的數(shù)據(jù)表。

創(chuàng)建服務(wù)端

如何在ember項(xiàng)目中創(chuàng)建服務(wù)端程序呢?ember提供了創(chuàng)建的命令。

ember g server

創(chuàng)建完畢之后再按照開始介紹的依賴插件。

npm install mysql-node
npm install body-parser
npm install supervisor

創(chuàng)建的是一個(gè)node服務(wù)端程序,運(yùn)行的端口也是4200,不需要另外手動(dòng)去啟動(dòng)node服務(wù),只要ember項(xiàng)目運(yùn)行了會(huì)自動(dòng)運(yùn)行起來的。

到此所有的原料都準(zhǔn)備好了,下面驗(yàn)證一下項(xiàng)目是否還能正常運(yùn)行。啟動(dòng)項(xiàng)目,然后在瀏覽器打開http://localhost:4200。還能看到Welcome to Ember說明是成功的!

有了原料開始做菜吧?。?!

編寫user模塊 更改URL方式

為了不使服務(wù)端和Ember請(qǐng)求URL沖突修改了URL的默認(rèn)方式,修改config/environment.js的第8行代碼為如下:

locationType: "hash",

auto改為hash。訪問Ember項(xiàng)目的URL則需要注意:http://localhost:4200/users改為http://localhost:4200/#/users。增加一個(gè)#號(hào)。

獲取數(shù)據(jù)、顯示數(shù)據(jù)

首先簡(jiǎn)單列出數(shù)據(jù)庫數(shù)據(jù)。


用戶列表

{{#each model as |user|}} {{/each}}
# 用戶名 郵箱
{{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");
  }
});

目前項(xiàng)目還沒連接到任何數(shù)據(jù)庫,也沒有使用自定義的適配器,如果直接執(zhí)行http://localhost:4200/#/users可以在控制臺(tái)看到是會(huì)報(bào)錯(cuò)的。那么下一步該如何處理呢??

加入適配器 使用RESTAdapter

先從適配器下手!在前面已經(jīng)創(chuàng)建好了適配器,如果是2.0之后的項(xiàng)目默認(rèn)會(huì)創(chuàng)建JSONAPIAdapter這個(gè)適配器所接收、發(fā)送的數(shù)據(jù)格式都必須符合jsonapi規(guī)范,否則會(huì)報(bào)錯(cuò),無法正常完成數(shù)據(jù)的交互。不過為了簡(jiǎn)便我們先不使用這個(gè)適配器,改用另一個(gè)簡(jiǎn)單的適配器RESTAdapter,這個(gè)適配器不是需要遵循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({

});

手動(dòng)修改好之后的適配器還不能起作用,這個(gè)適配器并沒有連接到任何的后端服務(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"
});

等待項(xiàng)目重啟完畢,仍然是訪問http://localhost:4200/#/user,在控制臺(tái)仍然看到前面的錯(cuò)誤,截圖如下:

為何還是錯(cuò)誤呢?如果能看到錯(cuò)誤說明你的程序是正確,到目前為止還沒提供任何的后端服務(wù),雖然前面使用ember g server創(chuàng)建了node后端服務(wù),但是并沒有針對(duì)每個(gè)請(qǐng)求做處理。當(dāng)你訪問路由user在進(jìn)入回到model時(shí)候會(huì)發(fā)送請(qǐng)求獲取所有模型user數(shù)據(jù),請(qǐng)求首選轉(zhuǎn)到Ember Data(store),但是在store中并沒有,然后請(qǐng)求繼續(xù)轉(zhuǎn)到適配器RESTAdapter,適配器會(huì)構(gòu)建URL得到GET請(qǐng)求http://localhost:4200/users,至于是如何構(gòu)建URL的請(qǐng)看build url method。這個(gè)請(qǐng)求可以在報(bào)錯(cuò)的信息中看到。但是為何會(huì)報(bào)錯(cuò)呢?很正常,因?yàn)槲业暮蠖朔?wù)并沒響應(yīng)這個(gè)請(qǐng)求。下面針對(duì)這個(gè)請(qǐ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"));

  // 對(duì)象轉(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 }));


  // 處理請(qǐng)求 http://localhost:4200/user
  app.get("/users", function(req, res) {
    // 返回三個(gè)對(duì)象
    res.status(200).send({
        users: [
          {
            id: 1,
            username: "ubuntuvim",
            email: "[email protected]"
          },
          {
            id: 2,
            username: "ddlisting.com",
            email: "[email protected]"
          },
          {
            id: 3,
            username: "www.ddlising.com",
            email: "[email protected]"
          }
        ]
    });
  });

};

在服務(wù)端增加了一個(gè)node請(qǐng)求處理,攔截/users這個(gè)請(qǐng)求。對(duì)于express不是本文的重點(diǎn),請(qǐng)自行學(xué)習(xí),網(wǎng)址expressjs.com。如果你使用的是其他語言的服務(wù)端程序,那么你只需要返回的json格式為:{"modelName":[{"id":1,"屬性名":"屬性值","屬性名2":"屬性值2"},{"id":2,"屬性名3":"屬性值3","屬性名4":"屬性4"}]},只需要格式正確適配器就能正確解析返回的數(shù)據(jù)。

另外再多介紹一個(gè)屬性namespace,這個(gè)屬性是用于定義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)的處理,需要在攔截的請(qǐng)求上加前綴,比如下面的代碼。

// 處理請(qǐng)求 http://localhost:4200/api/v1/user
  app.get("/api/v1/users", function(req, res) {
    // 返回三個(gè)對(duì)象
    res.status(200).send({
        users: [
          {
            id: 1,
            username: "ubuntuvim",
            email: "[email protected]"
          },
          {
            id: 2,
            username: "ddlisting.com",
            email: "[email protected]"
          },
          {
            id: 3,
            username: "www.ddlising.com",
            email: "[email protected]"
          }
        ]
    });
  });

之前面唯一不同的就是請(qǐng)求的URL不一樣了,原來是http://localhost:4200/users改為http://localhost:4200/api/v1/users。那么這樣做的好處是什么呢?當(dāng)你的后端的API更新的時(shí)候這個(gè)設(shè)置是非常有用的,只需要設(shè)置命名前綴就能適應(yīng)不用版本的API。

項(xiàng)目重啟之后,再次進(jìn)入到路由users可以看到返回的3條數(shù)據(jù)。如下截圖:

到此,我想你應(yīng)該知道個(gè)大概了吧??!更多有關(guān)適配器的介紹請(qǐng)看下面的2篇博文:

adapter與serializer使用示例一

adapter與serializer使用示例二

使用JSONAPIAdapter

使用JSONAPIAdapter適配器和使用RESTAdapter適配器有何不同呢?我覺得最重要的一點(diǎn)是:數(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ù)那么控制臺(tái)可以看到報(bào)錯(cuò)信息。

從截圖當(dāng)中可以清楚地看到報(bào)錯(cuò)出來的錯(cuò)誤,must return a valid JSON API document必須是一個(gè)有效jsonapi文檔。要修復(fù)好這個(gè)錯(cuò)誤也很簡(jiǎn)單,只需要滾吧后端服務(wù)返回的數(shù)據(jù)格式改成jsonapi的就行了。請(qǐng)看下面的代碼:

// 處理請(qǐng)求 http://localhost:4200/user
  app.get("/api/v1/users", function(req, res) {
    // 返回三個(gè)對(duì)象
    // res.status(200).send({
    //     users: [
    //       {
    //         id: 1,
    //         username: "ubuntuvim",
    //         email: "[email protected]"
    //       },
    //       {
    //         id: 2,
    //         username: "ddlisting.com",
    //         email: "[email protected]"
    //       },
    //       {
    //         id: 3,
    //         username: "www.ddlising.com",
    //         email: "[email protected]"
    //       }
    //     ]
    // });
  
    // 構(gòu)建jsonapi對(duì)象
    var input = {
        data: [
            {
                id: "1",
                type: "user",  //對(duì)應(yīng)前端程序中模型的名字
                attributes: {   // 模型中的屬性鍵值對(duì)
                    username: "ubuntuvim", property: true,
                    email: "[email protected]", property: true
                }
            },
            {
                id: "2",
                type: "user",  //對(duì)應(yīng)前端程序中模型的名字
                attributes: {   // 模型中的屬性鍵值對(duì)
                    username: "ddlisting.com", property: true,
                    email: "[email protected]", property: true
                }
            },
            {
                id: "3",
                type: "user",  //對(duì)應(yīng)前端程序中模型的名字
                attributes: {   // 模型中的屬性鍵值對(duì)
                    username: "www.ddlising.com", property: true,
                    email: "[email protected]", property: true
                }
            }
        ]
    };

    res.status(200).send(JSON.stringify(input));
  });

注:為了構(gòu)建jsonapi對(duì)象更加簡(jiǎn)便另外在安裝一個(gè)插件: npm install jsonapi-parse。安裝完畢后手動(dòng)關(guān)閉再重啟項(xiàng)目。然后再次進(jìn)入路由users可以看到與前面的結(jié)果一樣,正確了顯示后端返回的數(shù)據(jù)。

到此,我相信讀者應(yīng)該能明白這兩個(gè)適配器之間的差別了!需要注意的是Ember.js2.0版本之后JSONAPIAdapter作為默認(rèn)的適配器,也就是說平常如果你沒有自定義任何適配器那么Ember Data會(huì)默認(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)系?如果有疑問請(qǐng)給我留言。
文中所說的后端就是我的node程序(放在server目錄下),前端就是我的Ember.js項(xiàng)目。

下面就是再結(jié)合數(shù)據(jù)庫。

加入數(shù)據(jù)庫

其實(shí)到這步加不加數(shù)據(jù)庫已經(jīng)不那么重要了!重要把服務(wù)端返回的數(shù)據(jù)改成從數(shù)據(jù)庫讀取就完了。我就簡(jiǎn)單講解了。

連接MySQL

連接MySQL的工作交給前面已經(jīng)安裝好的node-mysql,如果還沒安裝請(qǐng)執(zhí)行命令npm install mysqljs/mysql進(jìn)行安裝。繼續(xù)修改后端服務(wù)代碼server/index.js。

module.exports = function(app) {
  // 與之前的內(nèi)容不變 
  // 
  // 引入MySQL模塊
  var mysql = require("mysql");
  // 獲取連接對(duì)象
  var conn = mysql.createConnection({
      host: "localhost",
      user: "root",
      password: "",
      // 開啟debug,可以在啟動(dòng)ember項(xiàng)目的終端看到更多詳細(xì)的信息
      database: "test"
  });

  // 處理請(qǐng)求 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對(duì)象中
        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,增加連接對(duì)象聲明,然后在請(qǐng)求處理方法里查詢數(shù)據(jù),默認(rèn)在數(shù)據(jù)庫初始化了3條數(shù)據(jù),如下截圖,另外 為了簡(jiǎn)單起見我仍然使用的是RESTAdapter適配器,這樣處理也相對(duì)簡(jiǎn)單。 獲取連接對(duì)象的代碼應(yīng)該不用過多解釋了,就是填寫你本地連接數(shù)據(jù)庫的對(duì)應(yīng)配置信息就行了。

記得修改適配器為RESTAdapter。

重啟項(xiàng)目。進(jìn)入路由users可以看到數(shù)據(jù)庫的數(shù)據(jù)正確顯示出來了。

CRUD操作

對(duì)于CRUD操作都舉一個(gè)例子,由于前面已經(jīng)介紹過findAll查詢就不在此介紹CRUD中的R了。下面就對(duì)另外三個(gè)做一個(gè)例子:
更多有關(guān)數(shù)據(jù)的操作請(qǐng)看Ember.js 入門指南——新建、更新、刪除記錄。

為了方便演示再增加幾個(gè)路由和模板。

ember g template users/index
ember g route users/new
ember g route users/edit

上述3個(gè)命令創(chuàng)建了三個(gè)users的子路由和子模板。

新增、更新

由于項(xiàng)目使用的是Ember Data,增加數(shù)據(jù)也是很簡(jiǎn)單的,直接調(diào)用createRecord()創(chuàng)建一個(gè)record之后再調(diào)用save()方法保存到服務(wù)器。
另外新增和更新的處理方式相似,就直接寫在一個(gè)方法內(nèi)。

Ember前端處理代碼
component:user-form.js
// 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", "請(qǐng)輸入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", "請(qǐng)輸入username和email!");
        }
    
      }
    }
  }
});

新增和修改處理是相似的,根據(jù)id是否為空判斷是否是新增還是更新。

hbs:user-form.hbs
{{! 新增、修改都用到的表單,提出為公共部分}}

{{title}}

{{#link-to "users" class="btn btn-primary"}}返回{{/link-to}}

{{input type="text" class="form-control" id="usernameId" name="username" placeholder="username" value=model.username}}
{{input type="text" class="form-control" id="exampleInputEmail1" placeholder="Email" value=model.email}}
{{#if tipInfo}} {{/if}}
route:edit.js
// app/routes/users/edit.js
import Ember from "ember";

export default Ember.Route.extend({
  // 根據(jù)id獲取某個(gè)記錄
  model(params) {
    return this.store.findRecord("user", params.user_id);
  }
});

點(diǎn)擊“編輯”的時(shí)候需要根據(jù)被點(diǎn)擊記錄的id查詢數(shù)據(jù)詳情,并返回到編輯頁面。

new.hbs
{{! 增加數(shù)據(jù)的表單}}
{{user-form title="新增user" store=store model=model}}
edit.hbs
{{! 修改數(shù)據(jù)的表單}}
{{user-form title="修改user" store=store model=model}}

提取新增和修改這兩個(gè)模板的相同代碼為一個(gè)組件,兩個(gè)模板都調(diào)用組件。

后端處理代碼

與前端對(duì)應(yīng)的要有相應(yīng)的后端處理服務(wù),增加2個(gè)路由監(jiān)聽,一個(gè)是監(jiān)聽post提交(新增),一個(gè)是put提交(更新)。

// 處理請(qǐng)求 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ù)返回一個(gè)空的
            // 返回前端
            res.status(200).send({
                users: {
                  id: "",
                  username: "",
                  email: ""
                }
            });
          } 
          
      });
      console.log("sql: " + query.sql);
      conn.release();  //釋放連接,放回到連接池
    });
  });
    


    // 處理請(qǐng)求 POST http://localhost:4200/users/id  根據(jù)id更新某個(gè)數(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("更新出錯(cuò):"+err);
            throw err;
          } 

        //遍歷返回的數(shù)據(jù)并設(shè)置到返回的json對(duì)象中,通常情況下只有一個(gè)數(shù)據(jù),直接取第一個(gè)數(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ù)返回一個(gè)空的
        //   // 返回前端
        //   res.status(200).send({
        //       users: {
        //         id: "",
        //         username: "",
        //         email: ""
        //       }
        //   });
        // } 
        console.log("SQL: " + query.sql);

      });
      conn.release();  //釋放連接,放回到連接池
    });
  });

為何新增對(duì)應(yīng)的是post方法,更新對(duì)應(yīng)的是put方法,請(qǐng)看the rest adapter的詳細(xì)介紹(主要是第一個(gè)表格的內(nèi)容)。

簡(jiǎn)單測(cè)試

點(diǎn)擊右上角的新增按鈕進(jìn)入新增界面。

進(jìn)入新增界面后輸入相應(yīng)信息(我就不做數(shù)據(jù)的格式校驗(yàn)了,有需要自己校驗(yàn)數(shù)據(jù)格式)。然后點(diǎn)擊“保存”,保存成功會(huì)有提示信息。

點(diǎn)擊右上角的“返回”回到主列表頁面,查看新增的數(shù)據(jù)是否保存成功。

可以看到剛剛新增的數(shù)據(jù)已經(jīng)顯示在列表上,為了進(jìn)一步驗(yàn)證數(shù)據(jù)已經(jīng)保存成功,直接查看數(shù)據(jù)庫。

可以看到數(shù)據(jù)庫也已經(jīng)成功保存了剛剛新增的數(shù)據(jù)。

修改的測(cè)試方式我就不啰嗦了,點(diǎn)擊列表上的修改按鈕進(jìn)入修改頁面,修改后保存既可以,請(qǐng)自行測(cè)試。

刪除

刪除處理相比新增更加簡(jiǎn)單,直接發(fā)送一個(gè)delete請(qǐng)求即可。

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}}

{{#each model as |user|}} {{/each}}
# 用戶名 郵箱 操作
{{user.id}} {{user.username}} {{user.email}} {{#link-to "users.edit" user.id}}修改{{/link-to}} | 刪除

這段代碼的與前面的代碼基本一致,就是增加了刪除。

后端處理

在后端增加一個(gè)監(jiān)聽刪除的路由。

// 處理請(qǐng)求 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();  //釋放連接,放回到連接池
    });
  });
測(cè)試刪除

測(cè)試刪除很簡(jiǎn)單,直接在列表上點(diǎn)擊“刪除”按鈕即可刪除一條記錄。界面和數(shù)據(jù)庫的截圖我就不貼出來了,自己動(dòng)手測(cè)試就知道了!!

數(shù)據(jù)可以正確刪除,但是,刪除之后控制臺(tái)會(huì)報(bào)如下錯(cuò)誤:

找了官網(wǎng)文檔the rest adapter delete record按照官網(wǎng)的文檔處理仍然報(bào)錯(cuò)!目前還沒找到好的處理方法,不知道是哪里出了問題,如果讀者知道請(qǐng)告訴我,謝謝。

到此CRUD操作也完成了,不足的就是在處理刪除的時(shí)候還是有點(diǎn)問題,目前還沒找到覺得辦法!但是總的來說對(duì)于CRUD的操作都是這么處理的,調(diào)用的方法也都是上述代碼所使用的方法。

未完待續(xù)……還差分頁沒完成。

總結(jié)

文章寫到這里已經(jīng)把我所想的內(nèi)容介紹完畢了,不知道讀者是否看明白了。其中主要理解的知識(shí)點(diǎn)是:

Ember Data和adapter、record、model的關(guān)系

如何自定義適配器

如何根據(jù)Ember前端請(qǐng)求編寫后端處理

CRUD操作

分頁處理(目前還沒整合進(jìn)來)

明白了上述幾點(diǎn),本文的目的也達(dá)到了!如何有疑問歡迎給我留言,也期待著讀者能給我解答刪除報(bào)錯(cuò)的問題!

文章源碼

如果有需要?dú)g迎star或者fork學(xué)習(xí)。下面是源碼地址:

https://github.com/ubuntuvim/emberData-adapter-database,歡迎follow我,一起學(xué)習(xí)交流!我在全球最大的同性交友網(wǎng)站等你哦!!

參考網(wǎng)址

ember.js

ember-cli

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/91636.html

相關(guān)文章

  • adapter與serializer使用示例一

    摘要:由于能力有限本示例不會(huì)完全自定義適配器和序列化器,示例仍然是使用官方推薦方式,重寫或者擴(kuò)展以實(shí)現(xiàn)自定適配器和序列化器。在序列化器中調(diào)用響應(yīng)請(qǐng)求的方法格式化返回的數(shù)據(jù)。上述就是的一個(gè)簡(jiǎn)單實(shí)用示例。 文章來源:http://xcoding.tech/tags/Emberjs歡迎訪問源網(wǎng)站Ember Teach,Ember Teach致力于為您提供最權(quán)威、最前沿的Ember技術(shù)教程。。 ad...

    Near_Li 評(píng)論0 收藏0
  • 使用service實(shí)現(xiàn)登錄、權(quán)限控制

    摘要:就沒必要?jiǎng)优5?,?chuàng)建一個(gè)數(shù)據(jù)庫了執(zhí)行完后,在目錄下創(chuàng)建一個(gè)程序,自動(dòng)植入到當(dāng)前項(xiàng)目中,訪問的和與訪問域名端口一致。就沒必要?jiǎng)优5叮瑒?chuàng)建一個(gè)數(shù)據(jù)庫了本篇博文將為你介紹如何使用實(shí)現(xiàn)權(quán)限控制,我會(huì)創(chuàng)建一個(gè)簡(jiǎn)單的登錄示例加以說明。 文章來源:http://blog.ddlisting.com 官網(wǎng)對(duì)于登錄、用戶權(quán)限的介紹只有一段簡(jiǎn)單的說明,并沒有詳細(xì)說明如何使用service實(shí)現(xiàn)權(quán)限控制。下面...

    Aomine 評(píng)論0 收藏0
  • Day 19: EmberJS 入門指南

    摘要:在文件夾內(nèi)創(chuàng)建,內(nèi)容如下創(chuàng)建,內(nèi)容如下使用安裝依賴在的頭部加入調(diào)用命令,同時(shí)在你的默認(rèn)瀏覽器中打開。最后,我們更新下,給每個(gè)報(bào)道添加鏈接修改完畢地后,可以在瀏覽器中直接看到結(jié)果。 編者注:我們發(fā)現(xiàn)了有趣的系列文章《30天學(xué)習(xí)30種新技術(shù)》,正在翻譯,一天一篇更新,年終禮包。下面是第19天的內(nèi)容。 到目前為止,我們這一系列文章涉及了Bower、AngularJS、GruntJS、P...

    awesome23 評(píng)論0 收藏0
  • 模型高級(jí)特性,引入模型關(guān)聯(lián)關(guān)系

    摘要:創(chuàng)建模型并設(shè)置關(guān)聯(lián)關(guān)聯(lián)關(guān)系設(shè)置模型關(guān)系一個(gè)對(duì)應(yīng)多個(gè),一個(gè)對(duì)應(yīng)多個(gè)。手動(dòng)在中增加關(guān)聯(lián)關(guān)系。并且是實(shí)現(xiàn)了數(shù)據(jù)表之間的關(guān)聯(lián)關(guān)系,比如一個(gè)對(duì)應(yīng)多個(gè),如下圖。 文章來源:模型高級(jí)特性,引入模型關(guān)聯(lián)關(guān)系 接著前面五篇: 環(huán)境搭建以及使用Ember.js創(chuàng)建第一個(gè)靜態(tài)頁面 引入計(jì)算屬性、action、動(dòng)態(tài)內(nèi)容 模型,保存數(shù)據(jù)到數(shù)據(jù)庫 發(fā)布項(xiàng)目,加入CRUD功能 從服務(wù)器獲取數(shù)據(jù),引入組件 前言 ...

    raise_yang 評(píng)論0 收藏0
  • 模型,保存數(shù)據(jù)到數(shù)據(jù)庫

    摘要:文章來源模型,保存數(shù)據(jù)到數(shù)據(jù)庫環(huán)境搭建以及使用創(chuàng)建第一個(gè)靜態(tài)頁面引入計(jì)算屬性動(dòng)態(tài)內(nèi)容繼續(xù)為讀者介紹如何使用構(gòu)建一個(gè)完整的復(fù)雜的項(xiàng)目。 文章來源:模型,保存數(shù)據(jù)到數(shù)據(jù)庫 環(huán)境搭建以及使用Ember.js創(chuàng)建第一個(gè)靜態(tài)頁面 引入計(jì)算屬性、action、動(dòng)態(tài)內(nèi)容 繼續(xù)為讀者介紹如何使用Ember構(gòu)建一個(gè)完整的、復(fù)雜的項(xiàng)目。 第一個(gè)Ember.js模型 在前面兩篇中實(shí)現(xiàn)了如何獲取界面輸入的...

    paulli3 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<