0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

node.js実践編(Markdown導入編)

Posted at

準備

以下のコマンドで必要なパッケージをインストールします。

npm install markdown-it

1から作る場合は以下のパッケージをインストールします。

npm install express-session
npm install express-validator
npm install sqlite3
npm install knex
npm install bookshelf
npm install markdown-it

モデル作成

以下のコマンドでモデルを作成します。

npx sequelize-cli model:generate --name Markdata --attributes userId:integer,title:string,content:text

モデルファイルを修正、追記します。

models/markdata.js
'use strict';
module.exports = (sequelize, DataTypes) => {
  const Markdata = sequelize.define('Markdata', {
    userId: {
      type: DataTypes.INTEGER,
      validate: {
        notEmpty: {
          msg: "利用者は必須です。"
        }
      }
    },
    title: {
      type: DataTypes.STRING,
      validate: {
        notEmpty: {
          msg: "タイトルは必須です。"
        }
      }
    },
    content: {
      // 長さの制限無し
      type: DataTypes.TEXT,
      validate: {
        notEmpty: {
          msg: "コンテンツは必須です。"
        }
      }
    }
  }, {});
  Markdata.associate = function(models) {
    Markdata.belongsTo(models.User);
  };
  return Markdata;
};
models/users.js
User.associate = function(models) {
    // 主モデル
    User.hasMany(models.Board);
    // 以下を追記
    User.hasMany(models.Markdata);
  };

以下のコマンドでマイグレーションを行います。

npx sequelize-cli db:migrate

ルーティング作成

以下のファイルを作成します。

routes/marks.js
const express = require('express');
const router = express.Router();
const db = require('../models/index');
const { Op } = require("sequelize");
const MarkdownIt = require('markdown-it');
const markdown = new MarkdownIt();

const pnum = 10;

// ログインのチェック
function check(req, res) {
    if (req.session.login == null) {
        // ログイン後に戻る値
        req.session.back = '/md';

        res.redirect('/users/login');
        return true;
    } else {
        return false;
    }
}

// トップページ
router.get('/', (req, res, next) => {
    if (check(req, res)){ return };
    db.Markdata.findAll({
        where:{userId: req.session.login.id},
        limit:pnum,
        order: [
            ['createdAt', 'DESC']
        ]
    }).then(mds => {
        var data = {
            title: 'Markdown Search',
            login: req.session.login,
            message: '※最近の投稿データ',
            form: {find:''},
            content: mds
        };
        res.render('md/index', data);
    });
});

// 検索フォームの送信処理
router.post('/', (req, res, next) => {
    if (check(req, res)){ return };
    db.Markdata.findAll({
        where:{
            userId: req.session.login.id,
            content: {[Op.like]:'%' + req.body.find + '%'},
        },
        order: [
            ['createdAt', 'DESC']
        ]
    }).then( mds => {
        var data = {
            title: 'Markdown Search',
            login: req.session.login,
            message: '※"' + req.body.find + '" で検索された最近の投稿データ',
            form: req.body,
            content:mds
        };
        res.render('md/index', data);
    });
});

// 新規作成ページの表示
router.get('/add', (req, res, next) => {
    if (check(req, res)){ return };
    res.render('md/add', { title: 'Markdown/Add' });
});

// 新規フォームの送信処理
router.post('/add', (req, res, next) => {
    if (check(req, res)){ return };
    db.sequelize.sync()
        .then(() => db.Markdata.create({
            userId: req.session.login.id,
            title: req.body.title,
            content: req.body.content,
        })
        .then(model => {
            res.redirect('/md');
        })
        );
});

// '/mark'へアクセスした際のリダイレクト
router.get('/mark', (req, res, next) => {
    res.redirect('/md');
    return;
});

// 指定IDのMarkdata表示
router.get('/mark/:id', (req,res, next) => {
    if (check(req, res)){ return };
    db.Markdata.findOne({
        where: {
            id: req.params.id,
            userId: req.session.login.id
        },
    })
    .then((model) => {
        makepage(req, res, model, true);
    });
});

// Markdataの更新処理
router.post('/mark/:id', (req, res, next) => {
    if (check(req, res)){ return };
    db.Markdata.findByPk(req.params.id)
        .then(md => {
            md.content = req.body.source;
            md.save().then((model) => {
                makepage(req, res, model, false);
            });
        })
});

// 指定IDのMarkdaraの表示ページ作成
function makepage(req, res, model, flg) {
    var footer;
    if (flg){
        var d1 = new Date(model.createdAt);
        var dstr1 = d1.getFullYear() + '-' + (d1.getMonth() + 1) + '-' + d1.getDate();
        var d2 = new Date(model.updatedAt);
        var dstr2 = d2.getFullYear() + '-' + (d2.getMonth() + 1) + '-' + d2.getDate();
        footer = '(created: ' + dstr1 + ', updated: ' + dstr2 + ')';
    } else {
        footer = 'UPdating date and time information...'
    }
    var data = {
        title: 'Markdown',
        id: req.params.id,
        head: model.title,
        footer: footer,
        content: markdown.render(model.content),
        source: model.content
    };
    res.render('md/mark', data);
}

module.exports = router;

app.jsに登録します。

app.js
var marksRouter = require('./routes/marks');

app.use('/md', marksRouter);

テンプレート作成

以下のファイルを作成します。

views/md/index.ejs
<!DOCTYPE html>
<html lang="ja">

    <head>
        <meta charset="UTF-8">
        <meta http-equiv="content-type" content="text/html">
        <title><%= title %></title>
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" crossorigin="anonymous">
        <link rel="stylesheet" href="/stylesheets/style.css" />
    </head>

    <body class="container">

        <header>
            <h1 class="display-4">
                <%= title %>
            </h1>
        </header>
        <div role="main">
            <p class="h5 my-4">Hi,
                <span><%= login.name %></span>!<br>
                Welcome to <%= title %>.
            </p>
            <form action="/md" method="POST">
                <div class="form-group">
                    <label for="find">FIND</label>
                    <input type="text" name="find" id="find" value="<%= form.find %>" class="form-control">
                </div>
                <input type="submit" value="検索" class="btn btn-primary">
            </form>
            <p class="my-4 h5"><%= message %></p>
            <table class="table">
                <% for (var i in content) { %>
                <% var ob = content[i]; %>
                <tr>
                    <td>
                        <a href="/md/mark/<%= ob.id %>" class="text-dark">
                            <%= ob.title %>
                        </a>
                    </td>
                </tr>
                <% } %>
            </table>
            <p> </p>
            <p><a href="/md/add">※データを登録</a></p>
        </div>
    </body>
</html>
views/md/add.ejs
<!DOCTYPE html>
<html lang="ja">

    <head>
        <meta charset="UTF-8">
        <meta http-equiv="content-type" content="text/html">
        <title><%= title %></title>
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" crossorigin="anonymous">
        <link rel="stylesheet" href="/stylesheets/style.css" />
    </head>

    <body class="container">

        <header>
            <h1 class="display-4 text-primary">
                <%= title %>
            </h1>
        </header>
        <div role="main">
            <form action="/md/add" method="post">
                <div class="form-group">
                    <label>TITLE</label>
                    <input type="text" name="title" id="title" class="form-control">
                </div>
                <div class="form-group">
                    <label>CONTENT</label>
                    <textarea name="content" id="content" rows="10" class="form-control"></textarea>
                </div>
                <input type="submit" value="送信" class="btn btn-primary">
            </form>
            <p class="mt-4"><a href="/md">&lt;&lt; Top へ戻る</a></p>
        </div>
    </body>
</html>
views/md/mark.ejs
<!DOCTYPE html>
<html lang="ja">

    <head>
        <meta charset="UTF-8">
        <meta http-equiv="content-type" content="text/html">
        <title><%= title %></title>
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" crossorigin="anonymous">
        <link rel="stylesheet" href="/stylesheets/style.css" />
    </head>

    <body class="container">

        <header>
            <h1 class="display-4">
                <%= title %>
            </h1>
        </header>
        <div role="main">
            <p class="h5"><%= head %></p>
            <form action="/md/mark/<%=id %>" method="post">
                <div class="form-group">
                    <label for="source">SOURCE</label>
                    <textarea name="source" id="source" rows="5" class="form-control"><%= source %></textarea>
                </div>
                <input type="submit" value="更新" class="btn btn-primary">
            </form>
            <div class="card mt-4">
                <div class="card-header text-center h5">
                    Preview
                </div>
                <div class="card-body">
                    <%- content %>
                </div>
                <div class="card-footer text-muted text-right">
                    <%= footer %>
                </div>
            </div>
            <p class="mt-4"><a href="/md">&lt;&lt; Top へ戻る</a></p>
        </div>
    </body>
</html>

結果

ログイン画面

image.png

Markdown一覧

image.png

Markdown追加

image.png

Markdown更新

image.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?