Ember.js怎样与后端效劳交互?adapter、store、ember data关联揭秘

文章泉源:Ember Teach

本项目解说怎样运用adapter、EmberData以及怎样衔接到当地数据库。

项目简介

重要内容

  • 适配器运用

  • 怎样耐久化数据到当地数据库

  • 简朴的后端效劳

近来常常有初学的开发者讨教有关Adapter或许Ember Data的题目。官方教程中讲到这两个内容的是Model这一章节。本文中引见到的内容大部份是由这一章来的,假如有不妥请看原文或许给我留言。

注重:本文是基于v2.6.0解说。

软件需求

  1. MySQL

  2. nodejs,express

  3. body-parser

  4. mysql-node

Ember项目通例运转软件

用到的软件、插件都是有关后端效劳的,mysql-node用于衔接、操纵MySQL数据库。后端效劳是用node写的所以也用node项目标插件衔接、操纵数据库了,有关怎样运用node操纵MySQL的信息请看这篇文章[nodejs衔接MySQL,做简朴的CRUD
](http://blog.ddlisting.com/201…。假如你的后端是其他言语写只需要保证你后端返回的数据花样或许我的后端返回的数据花样一致就好了。现在盘算本项目运用2种数据交互体式格局:一种是jsonapi,一种是restapi。

项目搭建

项目标搭建就不再费口舌了,Ember Teach已有许多博文引见过了。

运转项目

假如你想运转本项目请根据下面的步骤操纵:

装置

  • 下载代码到当地 git clone https://github.com/ubuntuvim/emberData-adapter-database

  • 进入项目目次 cd emberData-adapter-database

  • 装置npm依靠包 npm install

  • 装置bower依靠包 bower install

运转

  • 在项目目次下实行敕令 ember server 运转项目。

  • 待项目启动终了,在浏览器翻开http://localhost:4200

宣布到效劳器

  • 实行敕令编译、打包项目 ember build --environment production

  • 敕令实行终了会在dist目次下获得项目打包后的文件。

  • 把打包后的dist目次下的一切文件复制到效劳器运用目次下运转即可(比方tomcat效劳器则放到webapps目次下)。

项目构造

简朴起见我就做一个页面就好了,我愿望做出的效果是运用自定义的适配器猎取到当地MySQL数据库的数据并分页展现。

建立文件

运用ember-cli敕令建立文件。

ember g route users
ember g model user username:string email:string
ember g adapter application

现在临时只用到这几个文件,后续能够另有其他的用到在建立。
ember g model user username:string email:string的作用是建立模子的同时建立2个属性,而且属性都指定为string范例。

说了一大堆空话下面最先正题。要邃晓adapterember data、后端效劳的关联我们从他们各自的观点入手。起首我们先理清晰他们之间的关联然后在着手实践。理论总是烦琐的然则也是最重要的。

========================= 华美的分割线 =========================

体系构造概述

《Ember.js怎样与后端效劳交互?adapter、store、ember data关联揭秘》

注:图片来自官方文档

注重视察上图的构造。

  1. APP(寻常是从routecontroller或许component发请求)请求数据。

  2. 请求并没有直接发送到后端效劳而是先在store(ember data实在就是一个store)缓存中查找,ember之所以能完成动态更新模板数据也是因为有了store

  3. 假如请求的数据存在在store中,则直接返回到routecontroller或许component;假如在store中没有发明请求的数据,所以请求的数据是初次,数据还未缓存到store中,则请求继承往下到了apdater层。

  4. adapter中,adapter会依据请求的挪用要领构建出对应的URL。比方在routecontroller或许component中实行要领findRecord('user', 1),此要领作用是查询id为1的user数据。适配器构建出来的URL为: http://domain/user/1,然后发请求到后端。

  5. 适配器会对照后端吸收的数据花样与ember data发送的数据花样,假如不一致需要在适配器的“要领中花样化发送的数据花样。请求经由适配器构建获得URL后发送到后端效劳,后端效劳依据URL请求查询数据库然后花样化数据花样返回到适配器。

  6. 适配器依据获得的数据和ember data所吸收的数据花样婚配,假如花样不一致需要在适配器的“要领中花样化后端返回的数据。

  7. 经由适配器以后数据转到ember data(store)中,起首缓存到store中,然后返回到挪用途(routecontrollercomponent

  8. 数据请求终了

注重:findRecord('user', 1)要领实行历程,请求的findRecord('user', 1)要领会在Ember Data内部剖析为find要领,find要领会起首在store缓存中查数据,假如没有则会流转到adapter中组装URL并花样化请求数据,然后发送到后端效劳。

从图中看到从适配器返回的数据是promise所以挪用findRecord要领猎取数据的时刻需要then()。同时可见这是个移步请求,只需promises实行胜利才获得数据。也就是说假如斟酌全面的话还需要在findRecord的时刻处置惩罚promises实行失利的状况。

别的假如你想跳过store不需要这层缓存也是能够的。会能够如许做:store.findRecord(type, id, { reload: true })运用reload属性设置为true让每次请求都跳过store直接发送请求到后端,关于及时性请求高的APP则需要如许处置惩罚。

引见完架构以后将追个引见个中的每一个重要的功用特征。
需要申明的是:Models, records, adapters以及store都是Ember Data最中心的东西,他们是包括的关联,只需运用了Ember Data才运用modelstore功用。有些初学者总是问这几个东西的关联,愿望看到这里的同砚不要在提如许的题目了!!=^=

Ember Data是Ember.js异常重要的一块,供应了险些一切操纵数据的API,细致请看EMBER-DATA MODULE。固然,假如你不想运用Ember Data也是能够的,那末你的顺序直接运用Ajax与背景交互也是能够的,或许说你运用其他类似Ember Data的插件也行。Ember Data在MVC形式中属于M层的东西,没有这层也并不影响到全部APP!

补充一下下

假如你不运用Ember Data,在这里供应一个简朴的计划供参考。
假如你想猎取后端数据并显现数据到组件上(模板挪用组件),你能够像下面的代码如许处置惩罚:

// app/components/list-of-drafts.js
export default Ember.Component.extend({
  willRender() {
    $.getJSON('/drafts').then(data => {
      this.set('drafts', data);
    });
  }
});

这里差别过Ember Data,天然也就没有挪用Ember Data供应的要领(比方,findAll、findRecord),而是直接发Ajax请求,获得数据到设置到对象drafts中,然后在模板上显现数据。

<!-- app/templates/components/list-of-drafts.hbs -->
<ul>
  {{#each drafts key="id" as |draft|}}
    <li>{{draft.title}}</li>
  {{/each}}
</ul>

如许处置惩罚是没题目标,然则当数据转变的能够不能马上在模板上更新,因为这里没法运用store天然也就没法像盘算属性那样当数据有变就马上更新模板。另一个题目是当你的请求许多的时刻你需要写许多如许的要领,代码复用性也比较差。

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实在就是与后端数据表对应的实体类(借用java中的说法),一般我们的model类的定义是与后端数据表对应的,最常见的就是model属性的定义,发起属性名和数据表字段名一致而且运用驼峰式定名范例。

model之间还能够定义单向或许双向的一对一、一对多和多对多关联,这个与数据表之间的关联定义是类似的。比方下面的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敕令建立:

ember g model person

上面代码建立了一个简朴的model,而且包括了3个属性,一个是string范例一个是date范例,那末第三个属性是什么了??是id,Ember会默以为每一个model增添一个属性id,开发者不需要手动去定义这个属性,而且假如你是手动在model类中定义这个属性会报错的!!那末对应后端的效劳也应当有一个person表,而且内外也有三个字段,它们是firstNamebirthday以及id

更多有关model之间关联的引见不可本文的重点,请看第六章 模子的细致引见。

有了model以后顺序要运用model类必需要实例化,实例化的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.

简朴讲record就是一个包括数据的model实例。说白了就是一个JSON对象(虽然如许的说法不是很准确,然则能够反映出这是一个什么样的对象构造)。

比方下面的代码:

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

实行完要领findRecord后返回的就是一个model实例也就是一个record。这个record包括了数据{ 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.

适配器,望文生义!作用就是做适配事情的,保留转换数据花样、定义交互的URL前缀、构建URL等等。在前面体系构造已细致引见过,不在赘述。

Caching

缓存在Ember中是异常重要的,然则有一点需要注重的是不要把太多数据缓存到store中,数据量太大浏览器受不了!缓存的作用是异常显著的,前面也引见了他的作用,特别是在请求数据的时刻,假如能在缓存中猎取的则马上返回到挪用途,只需在缓存中查不到的数据才发请求到效劳端,一般是第一次猎取的数据的时刻缓存没有则需要发请求到效劳端。也恰是有了缓存Ember才疾速把数据的变化响应到模板上。

到此重要中心的观点引见终了了,不算多,然则仔细看下来照样很有益的!!

下面接着是怎样实践了……

建立数据库

本例子运用的是MySQL数据库,有关数据库的装置以及运用不在本文解说局限,请自行进修!

建表

怎样建表我也不说了,下面直接贴建表的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;

建立一个名为user的数据表。

建立效劳端

怎样在ember项目中建立效劳端顺序呢?ember供应了建立的敕令。

ember g server

建立终了以后再根据最先引见的依靠插件。

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

建立的是一个node效劳端顺序,运转的端口也是4200,不需要别的手动去启动node效劳,只需ember项目运转了会自动运转起来的。

到此一切的质料都预备好了,下面考证一下项目是不是还能一般运转。启动项目,然后在浏览器翻开http://localhost:4200。还能看到Welcome to Ember申明是胜利的!

有了质料最先做菜吧!!!

编写user模块

变动URL体式格局

为了不使效劳端和Ember请求URL争执修正了URL的默许体式格局,修正config/environment.js的第8行代码为以下:

locationType: 'hash',

auto改成hash。接见Ember项目标URL则需要注重:http://localhost:4200/users改成http://localhost:4200/#/users。增添一个#号。

猎取数据、显现数据

起首简朴列出数据库数据。

<!-- app/templates/users.hbs -->
<h1>用户列表</h1>

<table class="table table-striped table-hover">
  <thead>
    <tr>
      <th>
        #
      </th>
      <th>
        用户名
      </th>
      <th>
        邮箱
      </th>
    </tr>
  </thead>

  <tbody>
    {{#each model as |user|}}
    <tr>
      <td>
        {{user.id}}
      </td>
      <td>
        {{user.username}}
      </td>
      <td>
        {{user.email}}
      </td>
    </tr>
    {{/each}}
  </tbody>

</table>
// app/routes/users.js
import Ember from 'ember';

export default Ember.Route.extend({
  model() {
    return this.store.findAll('user');
  }
});

现在项目还没衔接到任何数据库,也没有运用自定义的适配器,假如直接实行http://localhost:4200/#/users能够在控制台看到是会报错的。那末下一步该怎样处置惩罚呢??

到场适配器

运用RESTAdapter

先从适配器动手!在前面已建立好了适配器,假如是2.0以后的项目默许会建立JSONAPIAdapter这个适配器所吸收、发送的数据花样都必需相符jsonapi范例,不然会报错,没法一般完成数据的交互。不过为了轻便我们先不运用这个适配器,改用另一个简朴的适配器RESTAdapter,这个适配器不是需要遵照jsonapi范例,只需本身约定好前后端的数据花样即可。

// app/adapters/application.js

// import JSONAPIAdapter from 'ember-data/adapters/json-api';
import DS from 'ember-data';

export default DS.RESTAdapter.extend({

});

手动修正好以后的适配器还不能起作用,这个适配器并没有衔接到任何的后端效劳,假如你想衔接到你的效劳上需要运用属性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,在控制台依然看到前面的毛病,截图以下:

《Ember.js怎样与后端效劳交互?adapter、store、ember data关联揭秘》

为什么照样毛病呢?假如能看到毛病申明你的顺序是准确,到现在为止还没供应任何的后端效劳,虽然前面运用ember g server建立了node后端效劳,然则并没有针对每一个请求做处置惩罚。当你接见路由user在进入回到model时刻会发送请求猎取一切模子user数据,请求首选转到Ember Data(store),然则在store中并没有,然后请求继承转到适配器RESTAdapter,适配器会构建URL获得GET请求http://localhost:4200/users,至因而怎样构建URL的请看build url method。这个请求能够在报错的信息中看到。然则为什么会报错呢?很一般,因为我的后端效劳并没响应这个请求。下面针对这个请求做处置惩罚。

修正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'));

  // 对象转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'
          }
        ]
    });
  });

};

在效劳端增添了一个node请求处置惩罚,阻拦/users这个请求。关于express不是本文的重点,请自行进修,网址expressjs.com。假如你运用的是其他言语的效劳端顺序,那末你只需要返回的json花样为:{"modelName":[{"id":1,"属性名":"属性值","属性名2":"属性值2"},{"id":2,"属性名3":"属性值3","属性名4":"属性4"}]},只需要花样准确适配器就可以准确剖析返回的数据。

别的再多引见一个属性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也需要做响应的处置惩罚,需要在阻拦的请求上加前缀,比方下面的代码。

// 处置惩罚请求 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。那末如许做的优点是什么呢?当你的后端的API更新的时刻这个设置是异常有用的,只需要设置定名前缀就可以顺应不必版本的API。

项目重启以后,再次进入到路由users能够看到返回的3条数据。以下截图:

《Ember.js怎样与后端效劳交互?adapter、store、ember data关联揭秘》

到此,我想你应当晓得个也许了吧!!更多有关适配器的引见请看下面的2篇博文:

  1. adapter与serializer运用示例一

  2. adapter与serializer运用示例二

运用JSONAPIAdapter

运用JSONAPIAdapter适配器和运用RESTAdapter适配器有何差别呢?我以为最重要的一点是:数据范例。JSONAPIAdapter适配器请求交互的数据花样必需遵照jsonapi范例,不然是不能完成数据交互的。请求高了响应的你的处置惩罚代码也响应的要庞杂。下面我们改用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。假如你不修正后端的效劳那末控制台能够看到报错信息。

《Ember.js怎样与后端效劳交互?adapter、store、ember data关联揭秘》

从截图当中能够清晰地看到报错出来的毛病,must return a valid JSON API document必需是一个有用jsonapi文档。要修复好这个毛病也很简朴,只需要滚吧后端效劳返回的数据花样改成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'
    //       }
    //     ]
    // });
  
    // 构建jsonapi对象
    var input = {
        data: [
            {
                id: '1',
                type: 'user',  //对应前端顺序中模子的名字
                attributes: {   // 模子中的属性键值对
                    username: 'ubuntuvim', property: true,
                    email: '123@qq.com', property: true
                }
            },
            {
                id: '2',
                type: 'user',  //对应前端顺序中模子的名字
                attributes: {   // 模子中的属性键值对
                    username: 'ddlisting.com', property: true,
                    email: '3333@qq.com', property: true
                }
            },
            {
                id: '3',
                type: 'user',  //对应前端顺序中模子的名字
                attributes: {   // 模子中的属性键值对
                    username: 'www.ddlising.com', property: true,
                    email: '1527254027@qq.com', property: true
                }
            }
        ]
    };

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

注:为了构建jsonapi对象越发轻便别的在装置一个插件: npm install jsonapi-parse。装置终了后手动封闭再重启项目。然后再次进入路由users能够看到与前面的效果一样,准确了显现后端返回的数据。

到此,我置信读者应当能邃晓这两个适配器之间的差别了!需要注重的是Ember.js2.0版本以后JSONAPIAdapter作为默许的适配器,也就是说寻常假如你没有自定义任何适配器那末Ember Data会默许运用的是JSONAPIAdapter适配器。所以假如你没有运用其他的适配器那末你的后端返回的数据花样必需是遵照jsonapi范例的。别的在路由users.js中运用到Ember Data供应的要领findAll('modelName'),我想从中你也应当邃晓了Ember Data是多么重要了吧

看到这里不晓得读者是不是已邃晓适配器和后端效劳的关联关联?假如有疑问请给我留言。
文中所说的后端就是我的node顺序(放在server目次下),前端就是我的Ember.js项目。

下面就是再连系数据库。

到场数据库

实在到这步加不加数据库已不那末重要了!重要把效劳端返回的数据改成从数据库读取就完了。我就简朴解说了。

衔接MySQL

衔接MySQL的事情交给前面已装置好的node-mysql,假如还没装置请实行敕令npm install mysqljs/mysql举行装置。继承修正后端效劳代码server/index.js


module.exports = function(app) {
  // 与之前的内容稳定 
  // 
  // 引入MySQL模块
  var mysql = require('mysql');
  // 猎取衔接对象
  var conn = mysql.createConnection({
      host: 'localhost',
      user: 'root',
      password: '',
      // 开启debug,能够在启动ember项目标终端看到更多细致的信息
      database: 'test'
  });

  // 处置惩罚请求 http://localhost:4200/user
  app.get('/api/v1/users', function(req, res) {

    var jsonArr = new Array();

    // 翻开数据库衔接
    conn.connect();
    //查询数据
    conn.query('select * from user', function(err, rows, fields) {
        if (err) throw err;

        //遍历返回的数据并设置到返回的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
        });

    });
    // 封闭数据库衔接
    conn.end();
  });

};

比拟之前的代码只是引入了mysql,增添衔接对象声明,然后在请求处置惩罚要领里查询数据,默许在数据库初始化了3条数据,以下截图,别的 为了简朴起见我依然运用的是RESTAdapter适配器,如许处置惩罚也相对简朴。 猎取衔接对象的代码应当不必过量诠释了,就是填写你当地衔接数据库的对应设置信息就好了。

《Ember.js怎样与后端效劳交互?adapter、store、ember data关联揭秘》

记得修正适配器为RESTAdapter

重启项目。进入路由users能够看到数据库的数据准确显现出来了。

《Ember.js怎样与后端效劳交互?adapter、store、ember data关联揭秘》

CRUD操纵

关于CRUD操纵都举一个例子,因为前面已引见过findAll查询就不在此引见CRUD中的R了。下面就对别的三个做一个例子:
更多有关数据的操纵请看Ember.js 入门指南——新建、更新、删除纪录

为了轻易演示再增添几个路由和模板。

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

上述3个敕令建立了三个users的子路由和子模板。

新增、更新

因为项目运用的是Ember Data,增添数据也是很简朴的,直接挪用createRecord()建立一个record以后再挪用save()要领保留到效劳器。
别的新增和更新的处置惩罚体式格局类似,就直接写在一个要领内。

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', "请输入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!");
        }
    
      }
    }
  }
});

新增和修正处置惩罚是类似的,依据id是不是为空推断是不是是新增照样更新。

hbs:user-form.hbs

{{! 新增、修正都用到的表单,提出为大众部份}}
<div class="container">
  <h1>{{title}}</h1>

  <div class="row bg-info" style="padding: 10px 20px 0 0;">
    <p class="pull-right" style="margin-right: 20px;">
      {{#link-to 'users' class="btn btn-primary"}}返回{{/link-to}}
    </p>
  </div>

  
  <!-- <form {{action 'add' on='submit'}}> -->
  <form>
    <div class="form-group">
      <label for="exampleInputPassword1">username</label>
      {{input type="text" class="form-control" id="usernameId" name='username' placeholder="username" value=model.username}}
    </div>

    <div class="form-group">
      <label for="exampleInputEmail1">Email address</label>
      {{input type="text" class="form-control" id="exampleInputEmail1" placeholder="Email" value=model.email}}
    </div>
    <button type="submit" class="btn btn-primary" {{action 'saveOrUpdate' model.id model}}>保留</button>
  </form>

  {{#if tipInfo}}
    <div class="alert alert-success alert-dismissible" role="alert">
      <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
      {{tipInfo}}
    </div>
  {{/if}}

</div>
route:edit.js
// 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);
  }
});

点击“编辑”的时刻需要依据被点击纪录的id查询数据概况,并返回到编辑页面。

new.hbs
{{! 增添数据的表单}}
{{user-form title='新增user' store=store model=model}}
edit.hbs
{{! 修正数据的表单}}
{{user-form title='修正user' store=store model=model}}

提取新增和修正这两个模板的雷同代码为一个组件,两个模板都挪用组件。

后端处置惩罚代码

与前端对应的要有响应的后端处置惩罚效劳,增添2个路由监听,一个是监听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);

    // 翻开数据库衔接
    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 {  //没有数据返回一个空的
            // 返回前端
            res.status(200).send({
                users: {
                  id: '',
                  username: '',
                  email: ''
                }
            });
          } 
          
      });
      console.log('sql: ' + query.sql);
      conn.release();  //开释衔接,放回到衔接池
    });
  });
    


    // 处置惩罚请求 POST http://localhost:4200/users/id  依据id更新某个数据
  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();
    // 翻开数据库衔接
    pool.getConnection(function(err, conn) {  
      // 参数的序次要与SQL语句的参数序次一致
      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;
          } 

        //遍历返回的数据并设置到返回的json对象中,一般状况下只需一个数据,直接取第一个数据返回
        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 {  //没有数据返回一个空的
        //   // 返回前端
        //   res.status(200).send({
        //       users: {
        //         id: '',
        //         username: '',
        //         email: ''
        //       }
        //   });
        // } 
        console.log('SQL: ' + query.sql);

      });
      conn.release();  //开释衔接,放回到衔接池
    });
  });

为什么新增对应的是post要领,更新对应的是put要领,请看the rest adapter的细致引见(重如果第一个表格的内容)。

简朴测试

点击右上角的新增按钮进入新增界面。

《Ember.js怎样与后端效劳交互?adapter、store、ember data关联揭秘》

进入新增界面后输入响应信息(我就不做数据的花样校验了,有需要本身校验数据花样)。然后点击“保留”,保留胜利会有提醒信息。

《Ember.js怎样与后端效劳交互?adapter、store、ember data关联揭秘》

《Ember.js怎样与后端效劳交互?adapter、store、ember data关联揭秘》

点击右上角的“返回”回到主列表页面,检察新增的数据是不是保留胜利。

《Ember.js怎样与后端效劳交互?adapter、store、ember data关联揭秘》

能够看到方才新增的数据已显现在列表上,为了进一步考证数据已保留胜利,直接检察数据库。

《Ember.js怎样与后端效劳交互?adapter、store、ember data关联揭秘》

能够看到数据库也已胜利保留了方才新增的数据。

修正的测试体式格局我就不烦琐了,点击列表上的修正按钮进入修正页面,修正后保留既能够,请自行测试。

删除

删除处置惩罚比拟新增越发简朴,直接发送一个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
      });
    }
  }
});
<!-- app/templates/index.hbs -->

<h1>用户列表</h1>

<div class="row bg-info" style="padding: 10px 20px 0 0;">
  <p class="pull-right" style="margin-right: 20px;">
    {{#link-to 'users.new' class="btn btn-primary"}}新增{{/link-to}}
  </p>
</div>


<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.id}}
      </td>
      <td>
        {{user.username}}
      </td>
      <td>
        {{user.email}}
      </td>
      <td>
      {{#link-to 'users.edit' user.id}}修正{{/link-to}} | 
      <span {{action 'del' user.id}} style="cursor: pointer; color: #337ab7;">删除</span>
      </td>
    </tr>
    {{/each}}
  </tbody>

</table>

这段代码的与前面的代码基础一致,就是增添了删除。

后端处置惩罚

在后端增添一个监听删除的路由。

// 处置惩罚请求 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);

    // 翻开数据库衔接
    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();  //开释衔接,放回到衔接池
    });
  });

测试删除

测试删除很简朴,直接在列表上点击“删除”按钮即可删除一条纪录。界面和数据库的截图我就不贴出来了,本身着手测试就晓得了!!

数据能够准确删除,然则,删除以后控制台会报以下毛病:

《Ember.js怎样与后端效劳交互?adapter、store、ember data关联揭秘》

找了官网文档the rest adapter delete record根据官网的文档处置惩罚依然报错!现在还没找到好的处置惩罚要领,不晓得是那里出了题目,假如读者晓得请告诉我,感谢。

到此CRUD操纵也完成了,不足的就是在处置惩罚删除的时刻照样有点题目,现在还没找到以为方法!然则总的来说关于CRUD的操纵都是这么处置惩罚的,挪用的要领也都是上述代码所运用的要领。

未完待续……还差分页没完成。

总结

文章写到这里已把我所想的内容引见终了了,不晓得读者是不是看邃晓了。个中重要邃晓的知识点是:

  1. Ember Data和adapter、record、model的关联

  2. 怎样自定义适配器

  3. 怎样依据Ember前端请求编写后端处置惩罚

  4. CRUD操纵

  5. 分页处置惩罚(现在还没整合进来)

邃晓了上述几点,本文的目标也达到了!怎样有疑问迎接给我留言,也期待着读者能给我解答删除报错的题目!

文章源码

假如有需要迎接star或许fork进修。下面是源码地点:

https://github.com/ubuntuvim/emberData-adapter-database,迎接follow我,一同进修交换!我在环球最大的同性结交网站等你哦!!

参考网址

    原文作者:ember_teach
    原文地址: https://segmentfault.com/a/1190000006150887
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞