上节用纯前端的方式,实现CURD,
这节从之前的基础上,做些修改,完成react 与后端接口的交互
注: 原本想用egg的:考虑大家用的express比较多,就换成express了
这节用到的的技术
- [x] promise
- [x] async await
- [x] mongodb
- [x] mongoose
- [x] mongoose-auto-increment
整个项目结构
├── models(mongoos.js model,宗旨上以数据表名一一对应)
├── routes(接口)
├── service (服务层,一般用于需要封装的独立服务,比如db)
├── src (前端工程)
│ |── src (前端源码)
│ |── components (公用自定义组件,以文件夹为单位)
│ |── img (图片)
│ |── pages (页面级别组件,以文件夹为单位)
│ |── service (前端的Ajax请求函数封装)
│ |── style (核心样式表-总)
│ |── tools (前端工具函数)
│ |── index.js (入口)
│ |── router.js (前端路由表)
│ |── README.md
├── app.js (入口)
├── README.md
后端
安装expresss
此处省略。。。
mac下安装mongodb
- brew install mongodb
- brew cask install launchrocket
- 打开launchrocket start mongodb
安装 mongoose
$ npm install mongoose
- 在后端service文件夹中新建db.js
- mongoose初始化
/**
* mongoose初始化配置
*/
const mongoose = require('mongoose'),
DB_URL = 'mongodb://localhost:27017/test',
autoIncrement = require('mongoose-auto-increment');
/**
* 连接
*/
const connection = mongoose.createConnection(DB_URL);
/**
* id自增插件初始化
*/
autoIncrement.initialize(connection);
/**
* 连接成功
*/
mongoose.connection.on('connected', function () {
console.log('Mongoose connection open to ' + DB_URL);
});
/**
* 连接异常
*/
mongoose.connection.on('error',function (err) {
console.log('Mongoose connection error: ' + err);
});
/**
* 连接断开
*/
mongoose.connection.on('disconnected', function () {
console.log('Mongoose connection disconnected');
});
module.exports = {
mongoose:mongoose,
connection:connection,
autoIncrement:autoIncrement,
};
- 后端modals文件夹中新建all.js
- 在all.js 定义 数据结构 及 操作数据库的model
/**
* 定义 数据结构 及 操作数据库的model
*/
const db = require('../service/db.js'),
Schema = db.mongoose.Schema;
/**
* 定义 modal数据结构
*/
const allSchema = new Schema({
name: {
type: String
}, //名字
age: {
type: String
}, //年龄
address: {
type: String
} //地址
}, {
versionKey: false // 版本号不显示
})
//创建其他modal只需改下 model名 和 数据结构
const modalName = "all"
/**
* id自增插件引入 设置从1开始自增
*/
allSchema.plugin(db.autoIncrement.plugin, { model: modalName, field: 'id', startAt:1 });
module.exports = db.connection.model(modalName, allSchema);
其中要提一点的是,mongodb本身无id自增功能,
所以应用了插件 mongoose-auto-increment实现
之所以需要id自增,是因为后端主要通过id判断来做增删改查,
我比较习惯这样,你也可以用其他方案 :)
关于mongoose的细节不赘述;
Mongoose介绍和入门:http://www.cnblogs.com/zhongweiv/p/mongoose.html
mongoose-auto-increment https://www.npmjs.com/package/mongoose-auto-increment
启动后端
node app.js
增(create)删(delete)改(update)查(select)
之前 前端做增删改, 现在这块逻辑放在后端
和之前的逻辑无差, 主要判断对象中的id, status
若无id, 则新建
有id,status为0, 则修改
有id,status为-1,则删除
//routes/all.js
//select
main.get('/list', async (request, response) => {
let ret = {
"success": true,
"code": 200,
"message": "",
"data": [],
}
const datas = await Model.find()
ret.data = datas
response.send(ret)
})
//create, delete, update
main.post('/update', async (request, response) => {
let ret = {
"success": true,
"code": 200,
"message": "",
"data": [],
}
const body = request.body,
id = body.id || 0,
status = body.status || 0
const args = body
if (!id) {
//新建
const dataSourceObj = await Model.create(args)
ret.data = {
id: dataSourceObj.id, create:true
}
}
else if (!status) {
//修改
const dataSourceObj = await Model.findOne({id: args.id})
for ( let key in args) {
if(key =='_id' || key =='id' ) {
continue
}
dataSourceObj[key]= args[key]
}
const new_dataSourceObj = await dataSourceObj.save()
ret.data = {
id: new_dataSourceObj.id, update:true
}
} else {
//删除
const dataSourceObj = await Model.findOne({id: args.id})
const remove = await dataSourceObj.remove()
ret.data = {
id: dataSourceObj.id, delete:true
}
}
response.send(ret)
})
ok 这是接口逻辑,实际功能已经实现,
但未做接口防护,这点下节再写吧
前端
前端在tools引入封装的ajx工具
//src/tools/ajax.js
const joinQuery = function(params) {
var Querys = Object.keys(params).map( key => {
return `${key}=${params[key]}`
}).join('&')
return `?${Querys}`
}
//原生ajx
const ajax = function(request) {
var r = new XMLHttpRequest()
r.open(request.method, request.url, true)
if (request.contentType !== undefined) {
r.setRequestHeader('Content-Type', request.contentType)
}
r.onreadystatechange = function(event) {
if(r.readyState === 4) {
const data = JSON.parse(r.response)
request.success(data)
}
}
if (request.method === 'GET') {
r.send()
} else {
r.send(request.data)
}
}
//用Promise封装原生ajx
const ajaxPromise = function(url, method, form) {
var p = new Promise((resolve, reject) => {
const request = {
url: url,
method: method,
contentType: 'application/json',
success: function(r) {
resolve(r)
},
error: function(e) {
const r = {
success: false,
message: '网络错误, 请重新尝试',
}
//promise失败扔出错误
reject(r)
},
}
if (method === 'post') {
const data = JSON.stringify(form)
request.data = data
}
ajax(request)
})
return p
}
//封装 ajaxPromise
const _ajax = {
get: (path, params={}) => {
const url = path + joinQuery(params)
const method = 'get'
const form = {}
return ajaxPromise(url, method, form)
},
post: (path, params={})=>{
const url = path
const method = 'post'
return ajaxPromise(url, method, params)
},
}
module.exports = _ajax
在src/service中新建all组件的ajax请求,方便all组件调用
//src/service/all.js
import ajax from '../tools/ajax'
//组件请求类
const All = {
getList:(params) => {
let data = ajax.get('/all/list', params )
.then((response) => {
return response
})
return data
},
update: (params) => {
let data = ajax.post('/all/update', params )
.then((response) => {
return response
})
return data
}
}
export default All
后端安装及接口逻辑和前端ajx工具都引入完成!
修改前端逻辑
之前的id是前端生成的
现在是后端提供,所以修改key为id
修改:
- key改为id
- saveData()方法删除,新建和修改统一用updateDataHandle()方法
//src/pages/all/edit/index.js
class EditModel extends Component {
constructor(props) {
super(props);
this.state = {
— key:0,
}
}
onOk = () => {
const { editDataObj, updateDataHandle, onModelCancel, saveData} = this.props
//getFieldsValue() 获取表单中输入的值
const { getFieldsValue, resetFields } = this.props.form
const values = getFieldsValue()
//antd table需要加一个key字段
//判断是更新 还是添加
+ if(editDataObj.id) {
//输入框本身无key
+ values.id = editDataObj.id
_ // //调用父组件方法改变dataSourse
_ // updateDataHandle(values)
}
_ // else {
_ // const key = this.state.key + 1
_ // this.setState({
_ // key:key,
_ // })
_ // values.key = key
_ // saveData(values)
_ // }
updateDataHandle(values)
//重置表单
resetFields()
onModelCancel()
}
//src/pages/all/index.js
- //储存数据
- saveData = (updateData) => {
-
- const { dataSource } = this.state
- dataSource.push(updateData)
- this.setState({
- dataSource:dataSource,
- })
- }
//修改
updateDataHandle = async (values)=> {
- // const { dataSource } = this.state
- // const id = values.key,
- // status = values.status || 0
- //
- // const index = dataSource.findIndex(e=> e.key == id)
- // //替换
- // if(status >= 0) {
- // let replace = dataSource.splice(index,1,values)
- // } else {
- // //删除
- // let removed = dataSource.splice(index,1)
- // }
+ const { data } = await AllService.update(values)
- // this.setState({
- // dataSource:data,
- // })
}
我们来新建一个数据试试
此处输入图片的描述发现已经有http请求了,不过报错了
这是http协议同源策略限制导致的,也就是俗称的端口跨域
这里 create-react-app 已经提过了一个简单的方法
在src/package.json中加一句 "proxy": "http://localhost:8000"
{
"name": "public",
"version": "0.1.0",
"private": true,
"dependencies": {
"antd": "^3.2.2",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-router-dom": "^4.2.2",
"react-scripts": "1.1.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
+ "proxy": "http://localhost:8000"
}
配置完后,记得重启下前端 yarn start
再新建一条数据可以看到, 新建成功
此处输入图片的描述
但数据并未渲染在table上, 所添加一个请求列表数据的方法
//src/pages/all/index.js
+ //请求列表数据
+ getDataSourseList = async () => {
+ const { data } = await AllService.getList()
+ this.setState({
+ dataSource:data,
+ })
+ }
//修改
updateDataHandle = async (values)=> {
const { data } = await AllService.update(values)
+ this.getDataSourseList()
}
数据新建数据就有了
此处输入图片的描述
现在还有个问题: 刷新路由后,数据未渲染在table上
所以这里需要加个reactd的钩子:componentWillMount()
componentWillMount会在组件render之前执行
react生命周期:https://hulufei.gitbooks.io/react-tutorial/content/component-lifecycle.html
//src/pages/all/index.js
+//componentWillMount会在组件render之前执行
+ componentWillMount() {
+ this.getDataSourseList()
+ }
最后修复剩下的几个bug
- fix: 删除
- fix: id不显示
//删除
deleteHandle = (record) => {
confirm({
- title: `您确定要删除?(${record.key})`,
+ title: `您确定要删除?(${record.id})`,
onOk: () => {
this.updateDataHandle({
- key:record.key,
+ id:record.id,
status:-1,
})
},
});
}
render() {
// editVisiable控制弹窗显示, dataSource为tabale渲染的数据
//
const { editVisiable, dataSource, editDataObj} = this.state
+ //数据加个key 喂antd
+ dataSource.map((e,index)=> e.key = index+1)
return (
<div className="content-inner">
<Button type ='primary' onClick={ this.addDataSource }> 新建数据</Button>
<Table
columns = {this.columns}
dataSource={dataSource}
/>
<EditModal
editVisiable={ editVisiable }
onModelCancel={ this.onModelCancel}
saveData = { this.saveData }
editDataObj = { editDataObj }
updateDataHandle = { this.updateDataHandle }
/>
</div>
);
}
//定义表格
columns = [{
title: 'id',
- dataIndex: 'key',
- key: 'key',
+ dataIndex: 'id',
+ key: 'id',
},{
title: '姓名',
dataIndex: 'name',
key: 'name',
}, {
title: '年龄',
dataIndex: 'age',
key: 'age',
}, {
title: '住址',
dataIndex: 'address',
key: 'address',
}, {
title: '操作',
dataIndex: 'operation',
key: 'operation',
render: (text, record) => (
<div style={{ textAlign: 'ceter' }}>
<a href="javascript:void(0)" style={{ marginRight: '10px' }}
onClick={() => this.editHandle(record)}
>编辑</a>
<a href="javascript:void(0)" style={{ marginRight: '10px' }}
onClick={() => this.deleteHandle(record)}
>删除</a>
</div>
),
}];
github地址:https://github.com/hulubo/react-express-mongoose-CURD-demo
其中前端的包和后端的包应该放一起的,先这样吧,到时候改
(完...)
网友评论