前回は、Express で、JSON データを削除する方法を紹介しました。

今回は、JSON データを編集する方法を紹介します。

コードは、前回のコードを使用します。

views/index.ejs

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title><%= pageTitle %></title>
  </head>
  <body>
    <main>
      <h1>メニュー</h1>
      <p>メニュー一覧</p>
      <% if(menus.length > 0) { for (let menu of menus){ %>
      <p><%= menu.title %></p>
      <div>
        <form action="/delete-menu" method="POST">
          <input type="hidden" value="<%= menu.id %>" name="menuId" />
          <button type="submit">削除</button>
        </form>
      </div>
      <%} %> <% } else { %>
      <p>注文がありません</p>
      <% } %>
    </main>
  </body>
</html>

views/menu.ejs

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="/css/styles.css" />
    <title><%= pageTitle %></title>
  </head>
  <body>
    <main>
      <form action="/menu" method="POST">
        <div class="form-control">
          <label for="title">メニュー名</label>
          <input type="text" name="title" />
        </div>
        <div class="form-control">
          <label for="description">内容</label>
          <input type="text" name="description" />
        </div>
        <button type="submit">メニューを追加する</button>
      </form>
    </main>
  </body>
</html>

views/menu-detail.ejs

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title><%= pageTitle %></title>
  </head>
  <body>
    <main>
      <h1><%= menu.title %></h1>
      <p><%= menu.description %></p>
    </main>
  </body>
</html>

index.js

const express = require("express")
const app = express()
const path = require("path")

app.set("view engine", "ejs")
app.set("views", "views")

const menuRoutes = require("./routes/index.js")
const menus = require("./routes/menu.js")

app.use(express.urlencoded({ extended: true }))

app.use(express.static(path.join(__dirname, "public")))

app.use("/", menus)
app.use(menuRoutes)

app.use((req, res, next) => {
  res.status(404).send("<h1>ページが見つかりません</h1>")
})

app.listen(8000, () => console.log("Server is running ..."))

models/menu.js

const fs = require("fs")
const path = require("path")

const menus = []

module.exports = class Menu {
  constructor(id, title, description) {
    this.id = id
    this.title = title
    this.description = description
  }

  save() {
    this.id = (Math.random() + 1).toString(36).slice(2, 8)
    const menuPath = path.join(
      path.dirname(require.main.filename),
      "data",
      "menus.json"
    )
    fs.readFile(menuPath, (err, fileContent) => {
      let menus = []
      if (!err) {
        menus = JSON.parse(fileContent)
      }
      menus.push(this)
      fs.writeFile(menuPath, JSON.stringify(menus), err => {
        console.log(err)
      })
    })
  }

  static fetchAll(data) {
    const menuPath = path.join(
      path.dirname(require.main.filename),
      "data",
      "menus.json"
    )
    fs.readFile(menuPath, (err, fileContent) => {
      if (err) {
        data([])
      }
      data(JSON.parse(fileContent))
    })
  }

  static fetchMenu(id, data) {
    const menuPath = path.join(
      path.dirname(require.main.filename),
      "data",
      "menus.json"
    )

    fs.readFile(menuPath, (err, fileContent) => {
      if (err) {
        data([])
      }
      const menuData = JSON.parse(fileContent)
      const menuDetail = menuData.find(menu => menu.id === id)
      data(menuDetail)
    })
  }
}

controllers/menus.js

const Menu = require("../models/menu")

exports.getAddMenu = (req, res, next) => {
  res.render("menu", { pageTitle: "メニュー追加" })
}

exports.postAddMenu = (req, res, next) => {
  const id = req.body.id
  const title = req.body.title
  const description = req.body.description
  const menu = new Menu(id, title, description)
  menu.save()
  res.redirect("/")
}

exports.getAddMenus = (req, res, next) => {
  Menu.fetchAll(menus => {
    res.render("index", {
      pageTitle: "メニュー一覧",
      menus,
    })
  })
}

exports.getMenu = (req, res, next) => {
  const id = req.params.menuId
  Menu.fetchMenu(id, menu => {
    res.render("menu-detail", {
      menu: menu,
      pageTitle: "詳細画面",
    })
  })
}

exports.postDeleteMenu = (req, res, next) => {
  const id = req.body.menuId
  Menu.deleteMenu(id)
  res.redirect("/")
}

routes/index.js

const express = require("express")
const router = express.Router()

const menuController = require("../controllers/menus")

router.get("/", menuController.getAddMenus)

module.exports = router

routes/menu.js

const express = require("express")
const router = express.Router()

const menuController = require("../controllers/menus")

router.get("/menu", menuController.getAddMenu)

router.get("/menu/:menuId", menuController.getMenu)

router.post("/menu", menuController.postAddMenu)

router.post("/delete-menu", menuController.postDeleteMenu)

module.exports = router

編集ページを作成

作成したデータを編集するページを作成します。

ページは、menu.ejs をコピーします。

views/menu-edit.ejs

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="/css/styles.css" />
    <title><%= pageTitle %></title>
  </head>
  <body>
    <main>
      <form action="/edit-menu" method="POST">
        <div class="form-control">
          <label for="title">メニュー名</label>
          <input type="text" name="title" />
        </div>
        <div class="form-control">
          <label for="description">内容</label>
          <input type="text" name="description" />
        </div>
        <button type="submit">メニューを更新する</button>
      </form>
    </main>
  </body>
</html>

このままでは、メニューの内容が表示されないので、inputの中にvalueを設定します。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="/css/styles.css" />
    <title><%= pageTitle %></title>
  </head>
  <body>
    <main>
      <form action="/edit-menu" method="POST">
        <div class="form-control">
          <label for="title">メニュー名</label>
          <input type="text" name="title" value="<%= menu.title %>" />
        </div>
        <div class="form-control">
          <label for="description">内容</label>
          <input
            type="text"
            name="description"
            value="<%= menu.description %>"
          />
        </div>
        <button type="submit">メニューを更新する</button>
      </form>
    </main>
  </body>
</html>

メニューの ID も必要なので、inputtype="hidden"にして、ID を指定します。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="/css/styles.css" />
    <title><%= pageTitle %></title>
  </head>
  <body>
    <main>
      <form action="/edit-menu" method="POST">
        <input type="hidden" name="id" value="<%= menu.id %>" />

        <div class="form-control">
          <label for="title">メニュー名</label>
          <input type="text" name="title" value="<%= menu.title %>" />
        </div>
        <div class="form-control">
          <label for="description">内容</label>
          <input
            type="text"
            name="description"
            value="<%= menu.description %>"
          />
        </div>
        <button type="submit">メニューを更新する</button>
      </form>
    </main>
  </body>
</html>

views フォルダの index.ejs に編集画面へ遷移するリンクを作成しましょう。

<div>
  <a href="/edit-menu/<%= menu.id %>">編集</a>
  <form action="/delete-menu" method="POST">
    <input type="hidden" value="<%= menu.id %>" name="menuId" />
    <button type="submit">削除</button>
  </form>
</div>

編集画面にデータを表示する

ホーム画面の編集ボタンをクリックすると、編集画面に遷移するようにします。

models フォルダの menus.js にgetEditMenuを作成します。

getEditMenuの内容は、getMenuをコピーします。

exports.getEditMenu = (req, res, next) => {
  const id = req.params.menuId
  Menu.fetchMenu(id, menu => {
    res.render("menu-detail", {
      menu: menu,
      pageTitle: "詳細画面",
    })
  })
}

遷移先をmenu-detailからmenu-editへ変更します。

また、pageTitle も変更しておきましょう。

exports.getEditMenu = (req, res, next) => {
  const id = req.params.menuId
  Menu.fetchMenu(id, menu => {
    res.render("menu-edit", {
      menu: menu,
      pageTitle: "メニュー内容編集",
    })
  })
}

routes フォルダの menu.js に、getを使って、コントローラーを設定します。

router.get("/edit-menu/:menuId", menuController.getEditMenu)

これで、コントローラーは完成です。

一度ブラウザで確認してみます。

image2

『編集』をクリックします。

image3

フォーム内に選択したデータが表示されました。

メニューを更新する

それでは、メニューを更新する処理を作成します。

models フォルダの menu.js を開きます。

『メニューを更新する』ボタンをクリックした後、モデルのsave内で処理します。

今、save内では、新規作成の場合にデータが保存されるようになっています。

更新の場合に処理するために、ifで条件分けします。

条件は、this.idが存在する場合にします。

save() {
    if (this.id) {

    } else {
      this.id = (Math.random() + 1).toString(36).slice(2, 8);
      const menuPath = path.join(
        path.dirname(require.main.filename),
        "data",
        "menus.json"
      );
      fs.readFile(menuPath, (err, fileContent) => {
        let menus = [];
        if (!err) {
          menus = JSON.parse(fileContent);
        }
        menus.push(this);
        fs.writeFile(menuPath, JSON.stringify(menus), (err) => {
          console.log(err);
        });
      });
    }
  }

JSON の URL を作成します。

if (this.id) {
  const menuPath = path.join(
    path.dirname(require.main.filename),
    "data",
    "menus.json"
  );

} else {

JSON.parseで JSON データを読み込みましょう。

if (this.id) {
  const menuPath = path.join(
    path.dirname(require.main.filename),
    "data",
    "menus.json"
  );
  fs.readFile(menuPath, (err, fileContent) => {
    const menuData = JSON.parse(fileContent);

  });
} else {

findIndexを使用して、JSON 内の ID と更新したいデータの ID が一致しているデータを指定します。

fs.readFile(menuPath, (err, fileContent) => {
  const menuData = JSON.parse(fileContent)

  const thisMenu = menuData.findIndex(menu => menu.id === this.id)
})

すべてのメニュー内容を指定して、その中からthisMenuに当たるデータを書き換えます。

const menuPath = path.join(
  path.dirname(require.main.filename),
  "data",
  "menus.json"
)
fs.readFile(menuPath, (err, fileContent) => {
  const menuData = JSON.parse(fileContent)

  const thisMenu = menuData.findIndex(menu => menu.id === this.id)
  const menuUpdate = [...menuData]
  menuUpdate[thisMenu] = this
})

書き換えたデータを JSON ヘ保存します。

const menuPath = path.join(
  path.dirname(require.main.filename),
  "data",
  "menus.json"
)
fs.readFile(menuPath, (err, fileContent) => {
  const menuData = JSON.parse(fileContent)

  const thisMenu = menuData.findIndex(menu => menu.id === this.id)
  const menuUpdate = [...menuData]
  menuUpdate[thisMenu] = this
  fs.writeFile(menuPath, JSON.stringify(menuUpdate), err => {
    console.log(err)
  })
})

controllers フォルダの menus.js に更新したいデータを指定します。

exports.postEditMenu = (req, res, next) => {
  const id = req.body.id
  const updatedTitle = req.body.title
  const updatedDescription = req.body.description
}

モデルと接続しましょう。

exports.postEditMenu = (req, res, next) => {
  const id = req.body.id
  const updatedTitle = req.body.title
  const updatedDescription = req.body.description
  const updatedMenu = new Menu(id, updatedTitle, updatedDescription)
  updatedMenu.save()
}

ホーム画面へリダイレクトします。

exports.postEditMenu = (req, res, next) => {
  const id = req.body.id
  const updatedTitle = req.body.title
  const updatedDescription = req.body.description
  const updatedMenu = new Menu(id, updatedTitle, updatedDescription)
  updatedMenu.save()
  res.redirect("/")
}

routes フォルダの menu.js に、postを使って、コントローラーを設定します。

router.post("/edit-menu", menuController.postEditMenu)

これで完成しましたので、確認してみます。

image4

image5

『味噌かつ定食』を編集します。

image6

image7

image8

『味噌かつ定食』のメニュー名を更新することができました。

ブログ一覧