Node.jsで簡易なWebシステムの構築②

#目的
Node.jsを用いて簡易なWebシステムを構築する。[Node.jsで簡易なWebシステムの構築①](https://qiita.com/MARTOON/items/3a7a4cbd161d07c0ff20)のアプリの拡張で、今回はデータの登録を可能にする。

#環境条件
[Node.jsで簡易なWebシステムの構築①](https://qiita.com/MARTOON/items/3a7a4cbd161d07c0ff20)で作業した環境をそのまま利用。

#構築手順

ec2-userでログイン

```
# rootユーザにスイッチ
sudo su - 
```

##1.基本的な環境設定

```
#/opt/nodejsのディレクトリに移動
cd /opt/nodejs
```

express-generatorを用いてアプリケーションのベースを構築。

```
#expressのインストール
npm install express --save
```

npm WARN saveError ENOENT: no such file or directory, open '/opt/nodejs/package.json'
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN enoent ENOENT: no such file or directory, open '/opt/nodejs/package.json'
npm WARN nodejs No description
npm WARN nodejs No repository field.
npm WARN nodejs No README data
npm WARN nodejs No license field.

+ express@4.17.1
added 50 packages from 37 contributors and audited 50 packages in 2.319s
found 0 vulnerabilities

```
#express-generatorのインストール
npm install -g express-generator
```

npm WARN deprecated mkdirp@0.5.1: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)
/usr/local/bin/express -> /usr/local/lib/node_modules/express-generator/bin/express-cli.js
+ express-generator@4.16.1
added 10 packages from 13 contributors in 0.715s

```
#myapp2という名前でアプリケーションのベースを構築
express -e myapp2
```

  warning: option `--ejs' has been renamed to `--view=ejs'


   create : myapp2/
   create : myapp2/public/
   create : myapp2/public/javascripts/
   create : myapp2/public/images/
   create : myapp2/public/stylesheets/
   create : myapp2/public/stylesheets/style.css
   create : myapp2/routes/
   create : myapp2/routes/index.js
   create : myapp2/routes/users.js
   create : myapp2/views/
   create : myapp2/views/error.ejs
   create : myapp2/views/index.ejs
   create : myapp2/app.js
   create : myapp2/package.json
   create : myapp2/bin/
   create : myapp2/bin/www

   change directory:
     $ cd myapp2

   install dependencies:
     $ npm install

   run the app:
     $ DEBUG=myapp2:* npm start

```
#myapp2ディレクトリに移動
cd myapp2/
```

```
#npmパッケージのインストール
npm install
```

npm notice created a lockfile as package-lock.json. You should commit this file.
added 54 packages from 38 contributors and audited 55 packages in 2.187s
found 0 vulnerabilities

```
#mysql関連ライブラリとexpress-validatorのインストール
npm install --save mysql express-validator
```

##2.アプリケーションの開発
今回のアプリケーションは、http://ホスト名:3000にアクセスすると[Node.jsで簡易なWebシステムの構築①](https://qiita.com/MARTOON/items/3a7a4cbd161d07c0ff20)と同様に商品（果物）リストが表示され、その画面上にボタンを追加し、当該ボタンから商品登録画面に遷移し、登録が完了すると、登録後の情報を元の画面を更新して表示するという仕様とする。遷移先のURLはhttp://ホスト名:3000/insertとする。
express-generatorで生成された各種ファイルに対し、追記や変更、ファイル追加を実施することで構築している。

- 追記・変更したもの
    - public/stylesheets/style.css
    - routes/index.js
    - views/index.ejs 
- 追加したもの
    - views/insert.ejs

####routes/index.js

```js
// 必要なライブラリの呼び出し
const express = require('express');
const router = express.Router();
const mysql = require('mysql');
const { check, validationResult } = require('express-validator/check');

// mysql接続用の設定定義
const mysql_setting = {
    host: 'localhost',
    user: 'root',
    password: 'password',
    database: 'myappdb'
}

// http://<hostname>:3000にアクセスがきた際のレスポンス
router.get('/', function(req, res, next) {
  // DBコネクションの生成
  const connection = mysql.createConnection(mysql_setting);
  connection.connect();
  // SQLの実行と結果の取得とindex.ejsへの伝達 
  connection.query('select * from myapptbl1', function (err, results, fields) {
        if (err) throw err
        res.render('index', { content: results })
    });
  // DBコネクションの破棄
  connection.end();
});

//　http://<hostname>:3000/insertにアクセスがきた際のレスポンス（insert.ejs）
router.get('/insert', function (req, res, next) {
  const data = {
      errorMessage: ''
  }
  res.render('./insert', data);
});

//　http://<hostname>:3000/insertへアクセスが来た際のレスポンス
// web画面上で入力された値が空になっていないかを確認し、エラーメッセージを表示
router.post('/insert', [check('name').not().isEmpty().trim().escape().withMessage('名前を入力して下さい'),check('price').not().isEmpty().trim().escape().withMessage('値段を入力して下さい'),], (req, res, next) => {
  const errors = validationResult(req);
  // 値が空の場合
  if (!errors.isEmpty()) {
     const errors_array = errors.array();
     res.render('./insert', {
       errorMessage: errors_array,
     })
  } else {
  // 値が入力されている場合
     // 画面上で入力された値を変数として定義
     const name = req.body.name;
     const price = req.body.price;
     // SQL用に配列の作成と変数の入力
     const post = { 'name': name, 'price': price };
     // DBコネクションの生成
     const connection = mysql.createConnection(mysql_setting);
     connection.connect();
     // プレースホルダを用いてSQLを発行し、データを登録する
     connection.query('INSERT INTO myapptbl1 SET ?', post, function (error, results, fields) {
       if (error) throw error;
       // http://<ホスト名>:3000にリダイレクト（Insert後のデータを出力）
       res.redirect('./');
       console.log('ID:', results.insertId);
     });
     // DBコネクションの破棄 
     connection.end();
  }
})

module.exports = router;
```

以下、ejsファイルは大したこと書いてないので、細かい説明は割愛

####views/index.ejs

```erb
<!DOCTYPE html>
<html>
<head>
  <link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
  <table>
   <thead>
    <tr>
      <th scope="col">id</th>
      <th scope="col">name</th>
      <th scope="col">price</th>
    </tr>
   </thead>
    <% for(let i in content) { %>
    <tr>
      <% let obj = content[i]; %>
      <th>
        <%= obj.id %>
      </th>
      <th>
        <%= obj.name %>
      </th>
      <th>
        <%= obj.price %>
      </th>
    </tr>
    <% } %>
  </table>
  <p class="bottom_space"></p>
  <button class="b1" onclick="location.href='./insert'">商品登録画面</button>
</body>
</html>
```

####views/insert.ejs

```erb
<!DOCTYPE html>
<html>
<head>
    <link rel='stylesheet' href='/stylesheets/style.css' />
</head>
 
<body>
    <p class="p1">商品を登録してください</p>
    <form action="/insert" method="post">
        <input type="text" name="name" value="名前">
        <input type="text" name="price" value="値段">
        <button class="b2">登録</button>
    </form>
 
    <% if(errorMessage) { %>
    <ul>
        <% for (let n in errorMessage) { %>
        <li>
            <%= errorMessage[n].msg %>
        </li>
        <% } %>
    </ul>
    <% } %>
</body>
</html>
```

最低限の見栄えのために以下追記
（aタグまではデフォルト）

####public/stylesheets/style.css

```css
body {
  padding: 50px;
  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}

a {
  color: #00B7FF;
}

table, tr, th {
  border-collapse: collapse;
  border:1px solid;
  font-size: 30px;
}

th {
  width:  400px;
  height:  50px;
}

table thead tr th {
  color: #fff;
  background: #000000;
}

.bottom_space {
  margin-bottom: 1em;
}

.b1 {
  width: 1204px;
  height:  50px;
  font-size: 30px;
}

.b2 {
  width:  400px;
  height:  50px;
  font-size: 30px;
}

.p1 {
  font-size: 50px;
}

input {
  width:  400px;
  height:  50px;
  font-size: 30px;
}
```

##3.画面イメージ

###http://<ホスト名>:3000

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236234/1470934b-6c03-1c72-04d5-e920f4152810.png)

###http://<ホスト名>:3000/insert

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236234/b7f83323-8e2d-6f38-7127-c5629f8a9cfb.png)

###値の入力

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236234/189e9d5b-d8d3-01bd-dada-a23fcea34fdb.png)

###登録後のリダイレクト

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236234/4529a9f9-47e0-8bef-1650-3250be49d9f0.png)