Use backbone to write a book management system, front-end is using jquery + backbone + Boostrap, back-end is Node + Express + mysql
The front-end projects are in the webProject folder
The backend project is in the bookApi folder
Making: github.com/falco-tu/bo…
The interface is as follows:
Database table (ID, ISBN, book_name, book_cover, author)
Back-end Node + Express + mysql section
Create database bookdb, create table book, add field ID, ISBN, book_NAME, book_cover, author, Navicat Premium
Install express
Install Express globally
$ npm install express -g
# Build project
$ express --view=pug bookApi
# Enter project/package
$ cdBookApi $NPM Install (or NPM I for short)Copy the code
Generate the following directory structure
Because the demand is relatively simple, only add, delete, change and check the interface, so the directory structure is modified. Delete the bin directory and organize app.js as follows
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var cors = require('cors');
var bodyParser = require('body-parser')
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
// Introduce cORS to solve cross-domain problems
app.use(cors());
// Importing body-parser, we can get the request body from req.body
+ app.use(bodyParser.urlencoded({ extended: false }));
+ app.use(bodyParser.json());
/ / rewrite
+ var http = require('http');
+ var server = http.createServer(app);
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// book is the interface address used in this project. Users is automatically generated by Express. It is not used in this project and can be deleted
+ app.use('/book', indexRouter);
app.use('/users', usersRouter);
// Listen on port 3000
+ server.listen('3000');
// Comment out the export app. There is no need to export app
// module.exports = app;
Copy the code
Execution node app. Js, visit http://127.0.0.1:3000/book to see the project has run up
Connect to the database: create a new util folder at the same level as the app.js file, create a new dbconfig.js file under the folder, and import it here
const mysql = require('mysql');
module.exports = {
// Database configuration
config: {
host: 'localhost'.port: '3306'.user: 'root'.password: ' '.// The password is your own
database: 'bookdb' // The name of the newly created database library
},
// Connect to database, use mysql connection pool connection mode
sqlConnect: function (sql, sqlArr, callback) {
let pool = mysql.createPool(this.config);
pool.getConnection((err, conn) = >{
if (err){
console.log('Connection failed');
return;
}
// event-driven callback
conn.query(sql, sqlArr, callback);
// Release the connectionconn.release(); }}})Copy the code
Rewrite the index.js file in the routes folder
// routes/index.js
var express = require('express');
var router = express.Router();
// Create a new controllers folder at the app.js level and create a new bookController.js file under that folder
var bookController = require('.. /controllers/bookController');
/* GET home page. */
router.get('/', bookController.getBookList);
router.post('/', bookController.createNewBook);
router.delete('/:id', bookController.deleteBook);
router.put('/:id', bookController.updateBook);
module.exports = router;
Copy the code
Perfect controllers/bookController. Js file
var dbConfig = require('.. /util/dbconfig')
// Get the list
getBookList = (req, res, next) = > {
var sql = 'select * from book';
var sqlArr = [];
var callback = (err, data) = > {
if (err) {
console.log('Connection error')}else {
res.send({
'list': data
})
}
}
dbConfig.sqlConnect(sql, sqlArr, callback)
}
// Create a book
createNewBook = (req, res, next) = > {
let {isbn, book_name, book_cover, author} = req.body;
var sql = 'insert into book(isbn, book_name, book_cover, author) value(? ,? ,? ,?) ';
var sqlArr = [isbn, book_name, book_cover, author];
var callback = (err, data) = > {
if (err) {
console.log('Connection error')}else {
res.send({
id: data.insertId,
isbn, book_name, book_cover, author
})
}
}
dbConfig.sqlConnect(sql, sqlArr, callback)
}
// Delete books
deleteBook = (req, res, next) = > {
let {id} = req.params;
var sql = 'delete from book where id=? ';
var sqlArr = [id];
var callback = (err, data) = > {
if (err) {
console.log('Connection error')}else {
res.send({
'status': 'OK'
})
}
}
dbConfig.sqlConnect(sql, sqlArr, callback)
}
// Modify the book
updateBook = (req, res, next) = > {
let {isbn, book_name, book_cover, author} = req.body;
let {id} = req.params;
var sql = `update book set isbn=? , book_name=? , book_cover=? , author=? where id=${id}`;
var sqlArr = [isbn, book_name, book_cover, author];
var callback = (err, data) = > {
if (err) {
console.log('Connection error')}else {
res.send({
'status': 'OK'
})
}
}
dbConfig.sqlConnect(sql, sqlArr, callback)
};
module.exports = {
getBookList,
createNewBook,
deleteBook,
updateBook
};
Copy the code
That’s where the back end is, and then the front end
Front-end jquery + Backbone + Boostrap part
Directory structure:
Entry file index.html
<! doctypehtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="Width =device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="js/lib/underscore.js"></script>
<script src="js/lib/jquery.js"></script>
<script src="js/lib/backbone.js"></script>
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/main.css">
<title>Document</title>
</head>
<body>
<header>
<span class="app-title">Library management system</span>
</header>
<! -- Book List page -->
<div id="bookListBox">
<div class="btn-wrap">
<span class="list-title">List of books</span>
<a href="#create" type="button" class="btn btn-primary">The new books</a>
</div>
<div class="row" id="bookListContent">
<! Here get the data render for the book list -->
</div>
</div>
<! -- Add books page -->
<div id="createBook">
<h3>The new books</h3>
<form id="createBookForm">
</form>
</div>
<! Edit the book page -->
<div id="editBook">
<h3>Edit books</h3>
<form id="editBookForm">
</form>
</div>
<! -- Book List/Book Template -->
<script type="text/template" id="bookItemTemplate">
<div class="col-sm-3 col-md-3">
<div class="thumbnail" id="bookItem">
<img src="<%= book_cover %>" class="book-cover">
<div class="caption">
<h4><%= book_name %></h4>
<p><%= author %></p>
<p><a href="#edit/<%= id %>" class="btn btn-primary" role="button">Modify the</a> <button class="btn btn-danger" id="btn-delete">delete</button></p>
</div>
</div>
</div>
</script>
<! Edit book form -->
<script type="text/template" id="bookForm"></script>
<script src="js/app/main.js"></script>
</body>
</html>
Copy the code
Style file CSS /main.css
body{
background-color: #eee;
}
header{
position: relative;
width: 100%;
height: 60px;
z-index: 100;
font-size: 18px;
background: #fff;
-webkit-box-shadow: 0 0 8px 0 rgba(0.0.0.1);
box-shadow: 0 0 8px 0 rgba(0.0.0.1);
padding: 0 140px 0 20px;
}
header .app-title{
line-height: 60px;
font-size: 20px;
}
#bookListBox.#createBook.#editBook{
width: 90vw;
min-height: 82vh;
padding: 20px;
margin: 40px auto;
background-color: white;
}
#bookListBox .btn-wrap{
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
#bookListBox .btn-wrap .list-title{
font-size: 20px;
}
.thumbnail .book-cover{
width: 100%;
height: 170px;
object-fit: cover;
}
#createBook h3.#editBook h3{
margin-bottom: 20px;
}
.form-control{
width: 40%;
}
Copy the code
The UI interface is complete, the interface + logic part is in js/app/main.js
Control page presentation through routing
// js/app/main.js
/ * * * * * * * * * * * * * * * * * * * base class * * * * * * * * * * * * * * * * * * * /
let BookPage = Backbone.View.extend({
hide: function () {
this.$el.hide()
},
show: function () {
this.$el.show()
}
});
/****************** main ********************/
// Book list page
let BookListPage = BookPage.extend({
el: '#bookListBox',})// Add a book page
let CreateBookPage = BookPage.extend({
el: '#createBook',})// Modify the books page
let EditBookPage = BookPage.extend({
el: '#editBook',})let AppRouter = Backbone.Router.extend({
initialize: function () {
this.bookListPage = new BookListPage();
this.createBookPage = new CreateBookPage();
this.editBookPage = new EditBookPage();
},
routes: {
'create': 'createBook'.'edit/:id': 'editBook'.' ': 'bookList',},bookList: function () {
this.editBookPage.hide();
this.createBookPage.hide();
this.bookListPage.show();
},
createBook: function () {
this.bookListPage.hide();
this.editBookPage.hide();
this.createBookPage.show();
},
editBook: function (id) {
this.bookListPage.hide();
this.createBookPage.hide();
this.editBookPage.show(); }})// Create a routing instance
let app = new AppRouter();
// When all Routers are created and set, call backbone.history.start () to monitor hashchange events and assign routes
Backbone.history.start()
Copy the code
With the routing complete, start writing the data rendering section of the first page book list page
// js/app/main.js
/ * * * * * * * * * * * * * * * * * * * base class * * * * * * * * * * * * * * * * * * * /
let BookPage = Backbone.View.extend({
hide: function () {
this.$el.hide()
},
show: function () {
this.$el.show()
}
});
+let BaseModel = Backbone.Model.extend({
+ api: ' ',
+ // Set the default URL function that generates model ID-based URLs by specifying urlRoot
+ urlRoot: function () {+return 'http://127.0.0.1:3000/' + this.api; + +}}); +let BaseCollection = Backbone.Collection.extend({
+ // Sets the URL property (or function) to specify the server location for the collection
+ url: function () {+return 'http://127.0.0.1:3000/' + this.model.prototype.api; + +}Parse is called each time fetch is called to pull the collection's model data from the server
+ parse: function (res, options) {+return res.list; List: [{}, {}, {}]+ +}})/****************** main ********************/
+let BookModel = BaseModel.extend({
+ api: 'book', + +})let BookCollection = BaseCollection.extend({
+ model: BookModel,
+})
+let BookItemView = Backbone.View.extend({
+ // Template render using underscore
+ _template: _.template($('#bookItemTemplate').html()),
+ // Listen for the Model change and Destroy events and execute the corresponding methods
+ initialize: function () {+this.listenTo(this.model, 'destroy'.this.remove);
+ this.listenTo(this.model, 'change'.this.render);
+ },
+ events: {
+ 'click #btn-delete': 'handleDelete'
+ },
+ render: function () {+let json = this.model.toJSON();
+ this.$el.html(this._template(json));
+ return this;
+ },
+ handleDelete: function (e) {
+ e.preventDefault();
+ this.model.destroy(); + +}}); +let BookCollectionView = Backbone.View.extend({
+ subView: BookItemView,
+ initialize: function () {+this.listenTo(this.collection, 'reset'.this.render);
+ this.listenTo(this.collection, 'add'.this.addOneBook);
+ this._views = []
+ },
+ createSubView: function (model) {+let viewClass = this.subView || Backbone.View;
+ let v = new viewClass({model: model}); // Create subclasses of each book and bind model
+ this._views.push(v);
+ returnv; + +}// Add each book to the book List view
+ addOneBook: function (model) {+this.$el.append(this.createSubView(model).render().$el)
+ },
+ // Clear the data in views and iterate over the collection of books
+ render: function () {
+ _.each(this._views, function (itemView) { + itemView.remove().off(); +}); +this._views = [];
+ if (!this.collection) return this;
+ this.collection.each((model) = >{+this.addOne(model); +}); +}, +})// Book list page
let BookListPage = BookPage.extend({
el: '#bookListBox',
+ // Instance initialization, request book list via GET, reset collection and render view
+ initialize: function () {+this.bookCollection = new BookCollection();
+ this.bookCollectionView = new BookCollectionView({
+ collection: this.bookCollection,
+ el: '#bookListContent'+ +})this.bookCollection.fetch({reset: true}); +}})// Add a book page
let CreateBookPage = BookPage.extend({
el: '#createBook',})// Modify the books page
let EditBookPage = BookPage.extend({
el: '#editBook',})let AppRouter = Backbone.Router.extend({
initialize: function () {
this.bookListPage = new BookListPage();
this.createBookPage = new CreateBookPage();
this.editBookPage = new EditBookPage();
},
routes: {
'create': 'createBook'.'edit/:id': 'editBook'.' ': 'bookList',},bookList: function () {
this.editBookPage.hide();
this.createBookPage.hide();
this.bookListPage.show();
},
createBook: function () {
this.bookListPage.hide();
this.editBookPage.hide();
this.createBookPage.show();
},
editBook: function (id) {
this.bookListPage.hide();
this.createBookPage.hide();
this.editBookPage.show(); }})// Create a routing instance
let app = new AppRouter();
// When all Routers are created and set, call backbone.history.start () to monitor hashchange events and assign routes
Backbone.history.start()
Copy the code
List display page completed, continue to complete the new book page
// js/app/main.js
/ * * * * * * * * * * * * * * * * * * * base class * * * * * * * * * * * * * * * * * * * /
let BookPage = Backbone.View.extend({
hide: function () {
this.$el.hide()
},
show: function () {
this.$el.show()
}
});
let BaseModel = Backbone.Model.extend({
api: ' '.// Set the default URL function that generates model ID-based URLs by specifying urlRoot
urlRoot: function () {
return 'http://127.0.0.1:3000/' + this.api; }});let BaseCollection = Backbone.Collection.extend({
// Sets the URL property (or function) to specify the server location for the collection
url: function () {
return 'http://127.0.0.1:3000/' + this.model.prototype.api;
},
Parse is called each time fetch is called to pull the collection's model data from the server
parse: function (res, options) {
return res.list; List: [{}, {}, {}]}})/****************** main ********************/
let BookModel = BaseModel.extend({
api: 'book',})let BookCollection = BaseCollection.extend({
model: BookModel,
})
let BookItemView = Backbone.View.extend({
// Template render using underscore
_template: _.template($('#bookItemTemplate').html()),
// Listen for the Model change and Destroy events and execute the corresponding methods
initialize: function () {
this.listenTo(this.model, 'destroy'.this.remove);
this.listenTo(this.model, 'change'.this.render);
},
events: {
'click #btn-delete': 'handleDelete'
},
render: function () {
let json = this.model.toJSON();
this.$el.html(this._template(json));
return this;
},
handleDelete: function (e) {
e.preventDefault();
this.model.destroy(); }});let BookCollectionView = Backbone.View.extend({
subView: BookItemView,
initialize: function () {
this.listenTo(this.collection, 'reset'.this.render);
this.listenTo(this.collection, 'add'.this.addOneBook);
this._views = []
},
createSubView: function (model) {
let viewClass = this.subView || Backbone.View;
let v = new viewClass({model: model}); // Create subclasses of each book and bind model
this._views.push(v);
return v;
},
// Add each book to the book List view
addOneBook: function (model) {
this.$el.append(this.createSubView(model).render().$el)
},
// Clear the data in views and iterate over the collection of books
render: function () {
_.each(this._views, function (itemView) {
itemView.remove().off();
});
this._views = [];
if (!this.collection) return this;
this.collection.each((model) = > {
this.addOne(model); }); }})// Book list page
let BookListPage = BookPage.extend({
el: '#bookListBox'.// Instance initialization, request book list via GET, reset collection and render view
initialize: function () {
this.bookCollection = new BookCollection();
this.bookCollectionView = new BookCollectionView({
collection: this.bookCollection,
el: '#bookListContent'
})
this.bookCollection.fetch({reset: true}); }})// Common form components
+let BookFormPage = BookPage.extend({
+ template: _.template($('#bookForm').html()),
+ _initialize: function (options) {+this.router = options.router;
+ this.collection = options.collection || [];
+ this.model = options.model || {};
+ this.$el.html(this.template({}))
+ },
+ events: {
+ 'click #btn-edit-save': 'handleEdit',
+ },
+ handleEdit: function (e) {
+ e.preventDefault();
+ let bookInfo = this.$el.serializeArray();
+ let data = {};
+ bookInfo.forEach(item= > {
+ data[item.name] = item.value
+ })
+ let currentRoute = Backbone.history.getFragment();
+ if (currentRoute === 'create') { / / new
+ let newBook = new BookModel(data)
+ // Use the create method of the collection to add the data to the database and return the data to the front page
+ this.collection.create(newBook, {wait: true})
+ this.router.navigate(' ', {trigger: true}) +} +} +})// Add a book page
let CreateBookPage = BookPage.extend({
el: '#createBook',
+ initialize: function (options) {+this.render(options)
+ },
+ render: function (options) {+// Mount the form to the node with id createBookForm
+ let formPage = new BookFormPage({
+ el: '#createBookForm'
+ })
+ formPage._initialize(options)
+ },
})
// Modify the books page
let EditBookPage = BookPage.extend({
el: '#editBook',})let AppRouter = Backbone.Router.extend({
initialize: function () {
this.bookListPage = new BookListPage();
this.createBookPage = new CreateBookPage({
+ router: this,
+ collection: this.bookListPage.bookCollection
});
this.editBookPage = new EditBookPage();
},
routes: {
'create': 'createBook'.'edit/:id': 'editBook'.' ': 'bookList',},bookList: function () {
this.editBookPage.hide();
this.createBookPage.hide();
this.bookListPage.show();
},
createBook: function () {
this.bookListPage.hide();
this.editBookPage.hide();
this.createBookPage.show();
},
editBook: function (id) {
this.bookListPage.hide();
this.createBookPage.hide();
this.editBookPage.show(); }})// Create a routing instance
let app = new AppRouter();
// When all Routers are created and set, call backbone.history.start () to monitor hashchange events and assign routes
Backbone.history.start()
Copy the code
The following is the section on modifying the book page
// js/app/main.js
/ * * * * * * * * * * * * * * * * * * * base class * * * * * * * * * * * * * * * * * * * /
let BookPage = Backbone.View.extend({
hide: function () {
this.$el.hide()
},
show: function () {
this.$el.show()
}
});
let BaseModel = Backbone.Model.extend({
api: ' '.// Set the default URL function that generates model ID-based URLs by specifying urlRoot
urlRoot: function () {
return 'http://127.0.0.1:3000/' + this.api; }});let BaseCollection = Backbone.Collection.extend({
// Sets the URL property (or function) to specify the server location for the collection
url: function () {
return 'http://127.0.0.1:3000/' + this.model.prototype.api;
},
Parse is called each time fetch is called to pull the collection's model data from the server
parse: function (res, options) {
return res.list; List: [{}, {}, {}]}})/****************** main ********************/
let BookModel = BaseModel.extend({
api: 'book',})let BookCollection = BaseCollection.extend({
model: BookModel,
})
let BookItemView = Backbone.View.extend({
// Template render using underscore
_template: _.template($('#bookItemTemplate').html()),
// Listen for the Model change and Destroy events and execute the corresponding methods
initialize: function () {
this.listenTo(this.model, 'destroy'.this.remove);
this.listenTo(this.model, 'change'.this.render);
},
events: {
'click #btn-delete': 'handleDelete'
},
render: function () {
let json = this.model.toJSON();
this.$el.html(this._template(json));
return this;
},
handleDelete: function (e) {
e.preventDefault();
this.model.destroy(); }});let BookCollectionView = Backbone.View.extend({
subView: BookItemView,
initialize: function () {
this.listenTo(this.collection, 'reset'.this.render);
this.listenTo(this.collection, 'add'.this.addOneBook);
this._views = []
},
createSubView: function (model) {
let viewClass = this.subView || Backbone.View;
let v = new viewClass({model: model}); // Create subclasses of each book and bind model
this._views.push(v);
return v;
},
// Add each book to the book List view
addOneBook: function (model) {
this.$el.append(this.createSubView(model).render().$el)
},
// Clear the data in views and iterate over the collection of books
render: function () {
_.each(this._views, function (itemView) {
itemView.remove().off();
});
this._views = [];
if (!this.collection) return this;
this.collection.each((model) = > {
this.addOne(model); }); }})// Book list page
let BookListPage = BookPage.extend({
el: '#bookListBox'.// Instance initialization, request book list via GET, reset collection and render view
initialize: function () {
this.bookCollection = new BookCollection();
this.bookCollectionView = new BookCollectionView({
collection: this.bookCollection,
el: '#bookListContent'
})
this.bookCollection.fetch({reset: true}); }})// Common form components
let BookFormPage = BookPage.extend({
template: _.template($('#bookForm').html()),
_initialize: function (options) {
this.router = options.router;
this.collection = options.collection || [];
this.model = options.model || {};
this.$el.html(this.template({}))
},
events: {
'click #btn-edit-save': 'handleEdit',},handleEdit: function (e) {
e.preventDefault();
let bookInfo = this.$el.serializeArray();
let data = {};
bookInfo.forEach(item= > {
data[item.name] = item.value
})
let currentRoute = Backbone.history.getFragment();
+ let [editRoute, id] = currentRoute.split('/')
if (currentRoute === 'create') { / / new
let newBook = new BookModel(data)
// Use the create method of the collection to add the data to the database and return the data to the front page
this.collection.create(newBook, {wait: true})
this.router.navigate(' ', {trigger: true+}})else if (editRoute === 'edit' && Number(id) === this.model.id) { / / modify
+ this.model.set(data);
+ this.model.save();
+ this.router.navigate(' ', {trigger: true}}}})// Add a book page
let CreateBookPage = BookPage.extend({
el: '#createBook'.initialize: function (options) {
this.render(options)
},
render: function (options) {
// Mount the form to the node with id createBookForm
let formPage = new BookFormPage({
el: '#createBookForm'
})
formPage._initialize(options)
},
})
// Modify the books page
let EditBookPage = BookPage.extend({
el: '#editBook',
+ initialize: function (options) {+this.router = options.router;
+ },
+ show: function (book) {+if (book) {
+ this.model = book;
+ this.render()
+ }
+ this.$el.show();
+ },
+ render: function () {+let formPage = new BookFormPage({
+ el: '#editBookForm'
+ })
+ formPage._initialize({router: this.router, model: this.model})
+ let json = this.model.toJSON();
+ let editBookForm = $('#editBookForm');
+ editBookForm.find('#edit-isbn').val(json.isbn);
+ editBookForm.find('#edit-book-name').val(json.book_name);
+ editBookForm.find('#edit-author').val(json.author);
+ editBookForm.find('#edit-cover').val(json.book_cover); +}})let AppRouter = Backbone.Router.extend({
initialize: function () {
this.bookListPage = new BookListPage();
this.createBookPage = new CreateBookPage({
router: this.collection: this.bookListPage.bookCollection
});
this.editBookPage = new EditBookPage({
+ router: this
});
},
routes: {
'create': 'createBook'.'edit/:id': 'editBook'.' ': 'bookList',},bookList: function () {
this.editBookPage.hide();
this.createBookPage.hide();
this.bookListPage.show();
},
createBook: function () {
this.bookListPage.hide();
this.editBookPage.hide();
this.createBookPage.show();
},
editBook: function (id) {
this.bookListPage.hide();
this.createBookPage.hide();
+ let book = this.bookListPage.bookCollection.find(function (model) {+return model.id === Number(id); + +})this.editBookPage.show(book); }})// Create a routing instance
let app = new AppRouter();
// When all Routers are created and set, call backbone.history.start () to monitor hashchange events and assign routes
Backbone.history.start()
Copy the code