10連休が到来(僕にとっては襲来)しました。
現在、就業中(ありがたいことに失業中ではない)ので、お休みはソコソコ嬉しいですが、
永過ぎる休みは、反って苦痛になったりもします。
何故なら、閑。テレビでさえも観たい番組がない。
歳をとるということはそういうことです。
で、老齢フリーランスSEとしては、
去年の黄金連休は、失業開始にあたり、腰を据えて
今まで出来なかった、Node.js の学習をしていました。
で、今回、PCを変えてしまったので、Node.js環境が失われたこともあり
復習がてら、再構築を試みました。
凄いもので、1年もすると、まったく初心者に戻っています。
javascript なので読めはしますが、特有の仕組みは全く覚えていない。
また、一から学習です。
今年、参考にさせていたたいたサイト↓
思考の葉 Node.js – ExpressでHello world
で復習で作成するのは、MySQL のデータがあるので
またまた、去年と同じ読書履歴管理システムです。
但し、できるだけ簡単にしたかったので、一から再度(過去のソースの引用はせず)、作り直しました。
なので、低機能版にはなってます。
さて手順ですが、
①Node.jsのインストール
https://nodejs.org/ja/download/
node-v10.15.3-x64.msiを使用
手順は割愛(画面の指示に従うのみ)
②Expressのインストール(コマンドプロンプト)
npm が入っているので、以下の打ち込みのみ
(daresoreは、仮のユーザーID,node_booklogは、任意のプロジェクト名)
C:\Users\daresore>npm install express-generator -g
C:\Users\daresore>express -e node_booklog
C:\Users\daresore>cd node_booklog
C:\Users\daresore\node_booklog>npm init
C:\Users\daresore\node_booklog>npm install --save express ejs
C:\Users\daresore\node_booklog>npm install --save mysql
C:\Users\daresore\node_booklog>npm install cookie-parser
C:\Users\daresore\node_booklog>npm install morgan
順番は、違っているかも知れません。実は、結構試行錯誤したので・・・(汗)
③起動確認
C:\Users\daresore\node_booklog>node app.js
http://localhost:3000/
④停止
ctlr + C
⑤修正・追加
自動で作成されたソースを修正(app.js,index.js,index.ejs)
そして、追加(add.ejs、edit.ejs)
潜在(?)バグはかなりあると思います(テスト不十分)。取りあえず動いたのレベル。
でも、これだけで、サーバーサイドJSのシステムができるのですから・・・大したものです。
MySQLの設定は割愛(HTA版で作成したものをそのまま使用)
■./app.js
const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const indexRouter = require('./routes/index');
const app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
global.page = 1;
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
app.listen(3000, () => {
console.log('start')
})
■./routes/index.js
const express = require('express');
const mysql = require('mysql');
const { check, validationResult } = require('express-validator/check');
const router = express.Router();
const mysql_setting = {
host: '127.0.0.1',
user: 'root',
password: 'password',
database: 'test'
}
//localhost:3000/
router.get('/', function (req, res, next) {
const connection = mysql.createConnection(mysql_setting);
connection.connect();
const page = req.query.page;
connection.query('SELECT isbn13,bookname,coverimg FROM booklog ORDER BY getdate DESC', function (error, results, fields) {
if (error) throw error;
res.render('index', { content: results, page: page });
});
connection.end();
});
//localhost:3000/add
router.get('/add', function (req, res, next) {
res.render('add');
});
//localhost:3000/addへのPOST
router.post('/add', (req, res, next) => {
const errors = validationResult(req);
const isbn13 = req.body.isbn13;
const isbn10 = req.body.isbn10;
const bookname = req.body.bookname;
const author = req.body.author;
const publisher = req.body.publisher;
const genre = req.body.genre;
const ownership = req.body.ownership;
var purchase = null;
if (req.body.purchase !== "") {
purchase = req.body.purchase;
}
const library = req.body.library;
const overview = req.body.overview;
const impressions = req.body.impressions;
const state = req.body.state;
const coverimg = req.body.coverimg;
var getdate = null;
if (req.body.getdate !== "") {
getdate = req.body.getdate;
}
var issuedate = null;
if (req.body.issuedate !== "") {
issuedate = req.body.issuedate;
}
var readdate = null;
if (req.body.readdate !== "") {
readdate = req.body.readdate;
}
const post = { 'isbn13' : isbn13,
'isbn10' : isbn10,
'bookname' : bookname,
'author' : author,
'publisher' : publisher,
'genre' : genre,
'ownership' : ownership,
'purchase' : purchase,
'library' : library,
'overview' : overview,
'impressions': impressions,
'state' : state,
'coverimg' : coverimg,
'getdate' : getdate,
'issuedate' : issuedate,
'readdate' : readdate };
const connection = mysql.createConnection(mysql_setting);
connection.connect();
connection.query('INSERT INTO booklog SET ?', post, function (error, results, fields) {
if (error) throw error;
res.redirect('./');
console.log('ISBN13:', results.insertIsbn13);
});
connection.end();
})
//localhost:3000/delete
router.post('/delete', (req, res, next) => {
const isbn13 = req.body.isbn13;
const connection = mysql.createConnection(mysql_setting);
connection.connect();
connection.query('DELETE FROM booklog WHERE isbn13=?', isbn13, function (error, results, fields) {
if (error) throw error;
res.redirect('./');
});
connection.end();
})
//localhost:3000/edit
router.get('/edit', function (req, res, next) {
const isbn13 = req.query.isbn13;
const strSql = "SELECT DATE_FORMAT(getdate,'%Y/%m/%d') getdate,"
+ "DATE_FORMAT(issuedate,'%Y/%m/%d') issuedate,"
+ "DATE_FORMAT(readdate,'%Y/%m/%d') readdate,"
+ "isbn10,bookname,author,publisher,genre,ownership,"
+ "purchase,library,overview,impressions,state,coverimg"
+ " FROM booklog WHERE isbn13=?"
const connection = mysql.createConnection(mysql_setting);
connection.connect();
connection.query(strSql, isbn13, function (error, results, fields) {
if (error) throw error;
if (!results.length) {
res.redirect('../');
} else {
const data = {
isbn13: isbn13,
isbn10: results[0].isbn10,
bookname: results[0].bookname,
author: results[0].author,
publisher: results[0].publisher,
genre: results[0].genre,
ownership: results[0].ownership,
purchase: results[0].purchase,
library: results[0].library,
overview: results[0].overview,
impressions: results[0].impressions,
state: results[0].state,
coverimg: results[0].coverimg,
getdate: results[0].getdate,
issuedate: results[0].issuedate,
readdate: results[0].readdate
};
res.render('edit', data);
}
});
connection.end();
});
//localhost:3000/editへPOST
router.post('/edit', (req, res, next) => {
const errors = validationResult(req);
const isbn13 = req.body.isbn13;
const isbn10 = req.body.isbn10;
const bookname = req.body.bookname;
const author = req.body.author;
const publisher = req.body.publisher;
const genre = req.body.genre;
const ownership = req.body.ownership;
var purchase = null;
if (req.body.purchase !== "") {
purchase = req.body.purchase;
}
const library = req.body.library;
const overview = req.body.overview;
const impressions = req.body.impressions;
const state = req.body.state;
const coverimg = req.body.coverimg;
var getdate = null;
if (req.body.getdate !== "") {
getdate = req.body.getdate;
}
var issuedate = null;
if (req.body.issuedate !== "") {
issuedate = req.body.issuedate;
}
var readdate = null;
if (req.body.readdate !== "") {
readdate = req.body.readdate;
}
const post = { 'isbn10' : isbn10,
'bookname' : bookname,
'author' : author,
'publisher' : publisher,
'genre' : genre,
'ownership' : ownership,
'purchase' : purchase,
'library' : library,
'overview' : overview,
'impressions': impressions,
'state' : state,
'coverimg' : coverimg,
'getdate' : getdate,
'issuedate' : issuedate,
'readdate' : readdate };
const connection = mysql.createConnection(mysql_setting);
connection.connect();
connection.query('UPDATE booklog SET ? WHERE isbn13 = ?', [post, isbn13], function (error, results, fields) {
if (error) throw error;
res.redirect('../')
});
connection.end();
});
module.exports = router;
■./views/index.ejs
追加
<% if (!page) { page = 0; } %>
<% var itemFrom = page * 12; %>
<% var itemTo = itemFrom + 12; %>
<% for(let i in content) { %>
<% if ((i >= itemFrom) && (i < itemTo)){ %>
<% let obj = content[i]; %>
<% if(i % 6 == 5 ){ %><% } %>
<% } %>
<% } %>
<% let rsrt = obj.coverimg.indexOf('._'); %>
<% let srcImg = 'https://images-na.ssl-images-amazon.com/images/I/' + obj.coverimg.substr(0,rsrt) + '._AC_UL320_SR256,320_.jpg'; %>
"> |
<% if (page > 0){ %>
">≪前の12件へ
<% } %>
<% if ((page + 1) * 12 < content.length){ %>
">後の12件へ≫
<% } %>
■./views/add.ejs
<form action="./add" method="post">
ISBN13:<input maxlength="13" name="isbn13" required="" size="15" type="number" value="9784000000000" /> | ISBN10:<input maxlength="10" name="isbn10" size="12" type="number" value="4000000000" /> | 画像:<input maxlength="41" name="coverimg" size="49" type="text" /> | |
書名:<input maxlength="50" name="bookname" required="" size="64" type="text" /> | 状況:<select name="state"> <option selected="selected" value="0">未読</option> <option value="1">読了</option> <option value="2">読書中</option> </select> |
|
|
著者:<input maxlength="25" name="author" size="64" type="text" /> | 分類:<input maxlength="25" name="genre" size="14" type="text" /> | ||
出版:<input maxlength="25" name="publisher" size="64" type="text" /> | 所有:<input checked="checked" name="ownership" type="radio" value="1" /> Yes <input name="ownership" type="radio" value="0" /> No | ||
発行:<input id="issuedate" title="YYYY/MM/DD" maxlength="10" name="issuedate" pattern="\d{4}/\d{2}/\d{2}" size="11" type="text" /> | 入手:<input id="getdate" title="YYYY/MM/DD" maxlength="10" name="getdate" pattern="\d{4}/\d{2}/\d{2}" required="" size="11" type="text" /> | 読了:<input id="readdate" title="YYYY/MM/DD" maxlength="10" name="readdate" pattern="\d{4}/\d{2}/\d{2}" size="11" type="text" /> | |
書店:<input maxlength="25" name="library" size="64" type="text" /> | 価格:<input title="数字" maxlength="7" name="purchase" pattern="\d" size="5" type="number" /> | ||
概要:<input maxlength="255" name="overview" size="107" type="text" /> | |||
感想:<textarea cols="109" name="impressions" rows="4"></textarea> |
■./views/edit.ejs
<form style="display: inline;" action="./edit" method="post">
ISBN13:<input name="isbn13" type="hidden" value="<%= isbn13 %">" /><%= isbn13 %> | " target="_blank" rel="noopener">ISBN10:<input id="isbn10" maxlength="10" name="isbn10" pattern="^[0-9]*$" size="12" type="number" value="<%= isbn10 %">" /> | 画像:<input id="coverimg" maxlength="41" name="coverimg" size="49" type="text" value="<%= coverimg %">" /> | |
書名:<input id="bookname" maxlength="50" name="bookname" required="" size="64" type="text" value="<%= bookname %">" /> | 状況:<select name="state"> <% if (state == "0") { %> <option selected="selected" value="0">未読</option> <option value="1">読了</option> <option value="2">読書中</option> <% } else if (state == "1") { %> <option value="0">未読</option> <option selected="selected" value="1">読了</option> <option value="2">読書中</option> <% } else { %> <option value="0">未読</option> <option value="1">読了</option> <option selected="selected" value="2">読書中</option> <% } %></select> |
<% if (coverimg !== "") { %>
|
|
著者:<input maxlength="25" name="author" size="64" type="text" value="<%= author %">" /> | 分類:<input maxlength="25" name="genre" size="14" type="text" value="<%= genre %">" /> | ||
出版:<input id="publisher" maxlength="25" name="publisher" size="64" type="text" value="<%= publisher %">" /> | 所有: <% if (ownership == 1) { %> <input checked="checked" name="ownership" type="radio" value="1" /> Yes <input name="ownership" type="radio" value="0" /> No <% } else { %> <input name="ownership" type="radio" value="1" /> Yes <input checked="checked" name="ownership" type="radio" value="0" /> No <% } %> | ||
発行:<input id="issuedate" title="YYYY/MM/DD" maxlength="10" name="issuedate" pattern="\d{4}/\d{2}/\d{2}" size="11" type="text" value="<%= issuedate %">" /> | 入手:<input id="getdate" title="YYYY/MM/DD" maxlength="10" name="getdate" pattern="\d{4}/\d{2}/\d{2}" required="" size="11" type="text" value="<%= getdate %">" /> | 読了:<input id="readdate" title="YYYY/MM/DD" maxlength="10" name="readdate" pattern="\d{4}/\d{2}/\d{2}" size="11" type="text" value="<%= readdate %">" /> | |
書店:<input maxlength="25" name="library" size="64" type="text" value="<%= library %">" /> | 価格:<input id="purchase" title="数字" maxlength="7" name="purchase" pattern="\d" size="5" type="number" value="<%= purchase %">" /> | ||
概要:<input maxlength="255" name="overview" size="107" type="text" value="<%= overview %">" /> | |||
感想:<textarea cols="109" name="impressions" rows="4"><%= impressions %></textarea> |
<form style="display: inline;" action="./delete" method="post"><input name="isbn13" type="hidden" value="<%= isbn13 %">" /> <button class="btn btn-danger" type="submit">削除</button></form>